tangning пре 3 година
родитељ
комит
ec91e52d59
63 измењених фајлова са 5143 додато и 1613 уклоњено
  1. 1 1
      .env.development
  2. 71 0
      src/api/operate/index.ts
  3. 75 0
      src/api/operate/model.ts
  4. 10 10
      src/api/sys/model/userModel.ts
  5. 8 3
      src/api/sys/user.ts
  6. 5 3
      src/enums/httpEnum.ts
  7. 1 1
      src/layouts/default/tabs/useMultipleTabs.ts
  8. 37 0
      src/locales/lang/zh-CN/routes/dashboard.ts
  9. 9 0
      src/locales/lang/zh-CN/routes/operate.ts
  10. 1 1
      src/main.ts
  11. 3 1
      src/router/routes/index.ts
  12. 0 80
      src/router/routes/modules/demo/charts.ts
  13. 0 564
      src/router/routes/modules/demo/comp.ts
  14. 0 324
      src/router/routes/modules/demo/feat.ts
  15. 0 28
      src/router/routes/modules/demo/flow.ts
  16. 0 48
      src/router/routes/modules/demo/iframe.ts
  17. 0 68
      src/router/routes/modules/demo/level.ts
  18. 0 255
      src/router/routes/modules/demo/page.ts
  19. 0 92
      src/router/routes/modules/demo/permission.ts
  20. 0 31
      src/router/routes/modules/demo/setup.ts
  21. 53 0
      src/router/routes/modules/operate.ts
  22. 93 0
      src/router/routes/modules/order.ts
  23. 6 6
      src/router/routes/modules/demo/system.ts
  24. 12 9
      src/store/modules/user.ts
  25. 99 0
      src/utils/encodeUtil.ts
  26. 5 8
      src/utils/http/axios/Axios.ts
  27. 50 49
      src/utils/http/axios/axiosTransform.ts
  28. 36 26
      src/utils/http/axios/index.ts
  29. 1 1
      src/views/demo/table/ExpandTable.vue
  30. 65 0
      src/views/operate/data.tsx
  31. 57 0
      src/views/operate/messageList.vue
  32. 222 0
      src/views/operate/newsList.vue
  33. 273 0
      src/views/operate/recruitList.vue
  34. 215 0
      src/views/order/cameraDetail.vue
  35. 180 0
      src/views/order/cameraList.vue
  36. 93 0
      src/views/order/confirmModal.vue
  37. 294 0
      src/views/order/data.tsx
  38. 215 0
      src/views/order/detail.vue
  39. 215 0
      src/views/order/downloadDetail.vue
  40. 212 0
      src/views/order/downloadList.vue
  41. 215 0
      src/views/order/equityDetail.vue
  42. 212 0
      src/views/order/equityList.vue
  43. 432 0
      src/views/order/list.vue
  44. 148 0
      src/views/order/printModal.vue
  45. 5 4
      src/views/sys/login/LoginForm.vue
  46. 62 0
      src/views/system/account/AccountDetail.vue
  47. 74 0
      src/views/system/account/AccountModal.vue
  48. 42 0
      src/views/system/account/DeptTree.vue
  49. 127 0
      src/views/system/account/account.data.ts
  50. 137 0
      src/views/system/account/index.vue
  51. 62 0
      src/views/system/dept/DeptModal.vue
  52. 108 0
      src/views/system/dept/dept.data.ts
  53. 103 0
      src/views/system/dept/index.vue
  54. 70 0
      src/views/system/menu/MenuDrawer.vue
  55. 110 0
      src/views/system/menu/index.vue
  56. 202 0
      src/views/system/menu/menu.data.ts
  57. 45 0
      src/views/system/password/index.vue
  58. 46 0
      src/views/system/password/pwd.data.ts
  59. 88 0
      src/views/system/role/RoleDrawer.vue
  60. 100 0
      src/views/system/role/index.vue
  61. 124 0
      src/views/system/role/role.data.ts
  62. 2 0
      types/axios.d.ts
  63. 12 0
      types/store.d.ts

+ 1 - 1
.env.development

@@ -6,7 +6,7 @@ VITE_PUBLIC_PATH = /
 
 # Cross-domain proxy, you can configure multiple
 # Please note that no line breaks
-VITE_PROXY = [["/basic-api","http://localhost:3000"],["/upload","http://localhost:3300/upload"]]
+VITE_PROXY = [["/basic-api","http://v4-test.4dkankan.com/manage"],["/upload","http://localhost:3300/upload"]]
 # VITE_PROXY=[["/api","https://vvbin.cn/test"]]
 
 # Delete console

+ 71 - 0
src/api/operate/index.ts

@@ -0,0 +1,71 @@
+import { defHttp } from '/@/utils/http/axios';
+import { PageParams, RentListGetResultModel, addCameraParams, updateParams } from './model';
+import { Result } from '/#/axios';
+
+enum Api {
+  pageList = '/service/manage/news/pageNews',
+}
+
+/**
+ * @description: Get sample list value
+ */
+
+export const ListApi = (params: PageParams) =>
+  defHttp.post<RentListGetResultModel>({
+    url: Api.pageList,
+    params: params,
+    // data: params,
+    headers: {
+      // @ts-ignore
+      ignoreCancelToken: true,
+    },
+  });
+
+// export const unDeviceApi = (params: PageParams) =>
+//   defHttp.post<RentListGetResultModel>({
+//     url: Api.unbindDevice,
+//     params,
+//     headers: {
+//       // @ts-ignore
+//       ignoreCancelToken: true,
+//     },
+//   });
+// export const allCompanyApi = (params: PageParams) =>
+//   defHttp.post<RentListGetResultModel>({
+//     url: Api.allCompany,
+//     params,
+//     headers: {
+//       // @ts-ignore
+//       ignoreCancelToken: true,
+//     },
+//   });
+
+// export const addCameraApi = (params: addCameraParams) =>
+//   defHttp.post<RentListGetResultModel>({
+//     url: Api.addCamera,
+//     params,
+//     headers: {
+//       // @ts-ignore
+//       ignoreCancelToken: true,
+//     },
+//   });
+
+// export const editCameraApi = (params: addCameraParams) =>
+//   defHttp.post<RentListGetResultModel>({
+//     url: Api.editCamera,
+//     params,
+//     headers: {
+//       // @ts-ignore
+//       ignoreCancelToken: true,
+//     },
+//   });
+
+// export const updateLiveRoomInfoApi = (params: updateParams) =>
+//   defHttp.post<Result>({
+//     url: Api.updateLiveRoomInfo,
+//     params,
+//     headers: {
+//       // @ts-ignore
+//       ignoreCancelToken: true,
+//     },
+//   });

+ 75 - 0
src/api/operate/model.ts

@@ -0,0 +1,75 @@
+import { BasicPageParams, BasicFetchResult } from '/@/api/model/baseModel';
+/**
+ * @description: Request list interface parameters
+ */
+export type PageParams = BasicPageParams;
+
+export interface addCameraParams {
+  address: string;
+  balance: string;
+  cameraType: string;
+  childName: string;
+  companyId?: string;
+  orderSn?: string;
+  own: string;
+  snCode?: string;
+  wifiName: string;
+}
+export interface updateParams {
+  id: number;
+  canShow: number;
+  liveRoomCapacities: number;
+}
+export interface DeviceListItem {
+  id: number;
+  activatedTime: string;
+  address: string;
+  agentFrameworkId: string;
+  agentFrameworkName: string;
+  agentName: string;
+  balance: string;
+  cameraType: number;
+  childName: string;
+  companyId: string;
+  companyName: string;
+  cooperationUser: string;
+  cooperationUserName: string;
+  country: number;
+  createTime: string;
+  goodsId: number;
+  goodsName: string;
+  imageUrl: string;
+  inTime: string;
+  isExpire: string;
+  lastTime: string;
+  nickName: string;
+  orderSn: string;
+  outTime: string;
+  own: number;
+  pic: string;
+  recStatus: string;
+  responseUserIncrement: string;
+  sceneNum: string;
+  snCode: string;
+  space: string;
+  spaceContent: string;
+  spaceEndStr: string;
+  spaceEndTime: string;
+  spaceId: string;
+  spaceStr: string;
+  surplusDate: string;
+  totalSpace: number;
+  totalSpaceStr: string;
+  type: string;
+  usedSpace: number;
+  usedSpaceStr: string;
+  userId: string;
+  userIncrementId: string;
+  userName: string;
+  wifiName: string;
+}
+
+/**
+ * @description: Request list return value
+ */
+export type RentListGetResultModel = BasicFetchResult<DeviceListItem>;

+ 10 - 10
src/api/sys/model/userModel.ts

@@ -2,8 +2,8 @@
  * @description: Login interface parameters
  */
 export interface LoginParams {
-  username: string;
-  password: string;
+  userName: string;
+  password?: string;
 }
 
 export interface RoleInfo {
@@ -15,24 +15,24 @@ export interface RoleInfo {
  * @description: Login interface return value
  */
 export interface LoginResultModel {
-  userId: string | number;
-  token: string;
-  role: RoleInfo;
+  id?: string | number;
+  token?: string;
+  roleId?: number;
 }
 
 /**
  * @description: Get user information return value
  */
 export interface GetUserInfoModel {
-  roles: RoleInfo[];
+  roleId?: string | number;
   // 用户id
-  userId: string | number;
+  id?: string | number;
   // 用户名
-  username: string;
+  userName?: string;
   // 真实名字
-  realName: string;
+  nickName?: string;
   // 头像
-  avatar: string;
+  avatar?: string;
   // 介绍
   desc?: string;
 }

+ 8 - 3
src/api/sys/user.ts

@@ -1,10 +1,10 @@
 import { defHttp } from '/@/utils/http/axios';
 import { LoginParams, LoginResultModel, GetUserInfoModel } from './model/userModel';
-
+import { encodeStr } from '/@/utils/encodeUtil';
 import { ErrorMessageMode } from '/#/axios';
 
 enum Api {
-  Login = '/login',
+  Login = '/service/manage/login',
   Logout = '/logout',
   GetUserInfo = '/getUserInfo',
   GetPermCode = '/getPermCode',
@@ -15,10 +15,15 @@ enum Api {
  * @description: user login api
  */
 export function loginApi(params: LoginParams, mode: ErrorMessageMode = 'modal') {
+  
+  const paramData: LoginParams = {
+    ...params,
+    password: encodeStr(window.btoa(params.password)),
+  };
   return defHttp.post<LoginResultModel>(
     {
       url: Api.Login,
-      params,
+      params:paramData,
     },
     {
       errorMessageMode: mode,

+ 5 - 3
src/enums/httpEnum.ts

@@ -1,9 +1,11 @@
 /**
  * @description: Request result set
  */
-export enum ResultEnum {
+ export enum ResultEnum {
   SUCCESS = 0,
-  ERROR = -1,
+  NORMAL = 200,
+  ERROR = 1,
+  JAVA_ERROR = -1,
   TIMEOUT = 401,
   TYPE = 'success',
 }
@@ -19,7 +21,7 @@ export enum RequestEnum {
 }
 
 /**
- * @description:  contentType
+ * @description:  contentTyp
  */
 export enum ContentTypeEnum {
   // json

+ 1 - 1
src/layouts/default/tabs/useMultipleTabs.ts

@@ -41,7 +41,7 @@ export function initAffixTabs(): string[] {
     }
   }
 
-  let isAddAffix = false;
+  let isAddAffix = true;
 
   if (!isAddAffix) {
     addAffixTabs();

+ 37 - 0
src/locales/lang/zh-CN/routes/dashboard.ts

@@ -3,4 +3,41 @@ export default {
   about: '关于',
   workbench: '工作台',
   analysis: '分析页',
+  corporation: '企业管理',
+  operate: '官网运营管理',
+  operateNews: '新闻管理',
+  operateRecruit: '招聘管理',
+  operateMessage: '留言管理',
+  corporationAccount: '企业账号',
+  corporationVerify: '企业认证',
+  bulletin: '信息发布',
+  bulletinRentInfo: '出租信息',
+  bulletinSellInfo: '出售信息',
+  bulletinDecoration: '工地装修',
+  advertisement: '广告位',
+  advertisementList: '轮播图',
+  advertisementPads: '推荐位',
+  scenes: 'VR场景管理',
+  scenesDownload: '场景下载',
+  scenesList: '场景列表',
+  scenesLive: '直播',
+  scenesRoom: '房间管理',
+  devices: '设备管理',
+  product: '商品管理',
+  productRef: '商品属性',
+  productList: '商品列表',
+  productCategory: '商品分类',
+  order: '订单管理',
+  orderList: '订单列表',
+  cameraList: '相机订单列表',
+  downloadList: '下载订单列表',
+  equityList: '权益订单列表',
+  orderDetail: '订单详情',
+  member: '会员管理',
+  memberList: '会员列表',
+  staff: '员工管理',
+  staffList: '员工列表',
+  staffClean: '清除状态',
+  feedback: '反馈管理',
+  feedbackList: '反馈列表',
 };

+ 9 - 0
src/locales/lang/zh-CN/routes/operate.ts

@@ -0,0 +1,9 @@
+export default {
+  releaseTime:'发布时间',
+  newsTitle:'新闻标题',
+  newsAdd:'新增新闻',
+  source:'来源',
+  objTitle:'职位名称',
+  submitTitle:'提交时间',
+  mesgContent:'留言内容',
+};

+ 1 - 1
src/main.ts

@@ -42,7 +42,7 @@ async function bootstrap() {
 
   // router-guard
   // 路由守卫
-  setupRouterGuard(router);
+  // setupRouterGuard(router);
 
   // Register global directive
   // 注册全局指令

+ 3 - 1
src/router/routes/index.ts

@@ -17,8 +17,10 @@ Object.keys(modules).forEach((key) => {
   routeModuleList.push(...modList);
 });
 
+console.log('PAGE_NOT_FOUND_ROUTE',PAGE_NOT_FOUND_ROUTE)
+console.log('routeModuleList',routeModuleList)
 export const asyncRoutes = [PAGE_NOT_FOUND_ROUTE, ...routeModuleList];
-
+console.log('asyncRoutes',asyncRoutes)
 // 根路由
 export const RootRoute: AppRouteRecordRaw = {
   path: '/',

+ 0 - 80
src/router/routes/modules/demo/charts.ts

@@ -1,80 +0,0 @@
-import type { AppRouteModule } from '/@/router/types';
-
-import { getParentLayout, LAYOUT } from '/@/router/constant';
-import { t } from '/@/hooks/web/useI18n';
-
-const charts: AppRouteModule = {
-  path: '/charts',
-  name: 'Charts',
-  component: LAYOUT,
-  redirect: '/charts/echarts/map',
-  meta: {
-    orderNo: 500,
-    icon: 'ion:bar-chart-outline',
-    title: t('routes.demo.charts.charts'),
-  },
-  children: [
-    {
-      path: 'baiduMap',
-      name: 'BaiduMap',
-      meta: {
-        title: t('routes.demo.charts.baiduMap'),
-      },
-      component: () => import('/@/views/demo/charts/map/Baidu.vue'),
-    },
-    {
-      path: 'aMap',
-      name: 'AMap',
-      meta: {
-        title: t('routes.demo.charts.aMap'),
-      },
-      component: () => import('/@/views/demo/charts/map/Gaode.vue'),
-    },
-    {
-      path: 'googleMap',
-      name: 'GoogleMap',
-      meta: {
-        title: t('routes.demo.charts.googleMap'),
-      },
-      component: () => import('/@/views/demo/charts/map/Google.vue'),
-    },
-
-    {
-      path: 'echarts',
-      name: 'Echarts',
-      component: getParentLayout('Echarts'),
-      meta: {
-        title: 'Echarts',
-      },
-      redirect: '/charts/echarts/map',
-      children: [
-        {
-          path: 'map',
-          name: 'Map',
-          component: () => import('/@/views/demo/charts/Map.vue'),
-          meta: {
-            title: t('routes.demo.charts.map'),
-          },
-        },
-        {
-          path: 'line',
-          name: 'Line',
-          component: () => import('/@/views/demo/charts/Line.vue'),
-          meta: {
-            title: t('routes.demo.charts.line'),
-          },
-        },
-        {
-          path: 'pie',
-          name: 'Pie',
-          component: () => import('/@/views/demo/charts/Pie.vue'),
-          meta: {
-            title: t('routes.demo.charts.pie'),
-          },
-        },
-      ],
-    },
-  ],
-};
-
-export default charts;

+ 0 - 564
src/router/routes/modules/demo/comp.ts

@@ -1,564 +0,0 @@
-import type { AppRouteModule } from '/@/router/types';
-
-import { getParentLayout, LAYOUT } from '/@/router/constant';
-import { t } from '/@/hooks/web/useI18n';
-
-const comp: AppRouteModule = {
-  path: '/comp',
-  name: 'Comp',
-  component: LAYOUT,
-  redirect: '/comp/basic',
-  meta: {
-    orderNo: 30,
-    icon: 'ion:layers-outline',
-    title: t('routes.demo.comp.comp'),
-  },
-
-  children: [
-    {
-      path: 'basic',
-      name: 'BasicDemo',
-      component: () => import('/@/views/demo/comp/button/index.vue'),
-      meta: {
-        title: t('routes.demo.comp.basic'),
-      },
-    },
-
-    {
-      path: 'form',
-      name: 'FormDemo',
-      redirect: '/comp/form/basic',
-      component: getParentLayout('FormDemo'),
-      meta: {
-        // icon: 'mdi:form-select',
-        title: t('routes.demo.form.form'),
-      },
-      children: [
-        {
-          path: 'basic',
-          name: 'FormBasicDemo',
-          component: () => import('/@/views/demo/form/index.vue'),
-          meta: {
-            title: t('routes.demo.form.basic'),
-          },
-        },
-        {
-          path: 'useForm',
-          name: 'UseFormDemo',
-          component: () => import('/@/views/demo/form/UseForm.vue'),
-          meta: {
-            title: t('routes.demo.form.useForm'),
-          },
-        },
-        {
-          path: 'refForm',
-          name: 'RefFormDemo',
-          component: () => import('/@/views/demo/form/RefForm.vue'),
-          meta: {
-            title: t('routes.demo.form.refForm'),
-          },
-        },
-        {
-          path: 'advancedForm',
-          name: 'AdvancedFormDemo',
-          component: () => import('/@/views/demo/form/AdvancedForm.vue'),
-          meta: {
-            title: t('routes.demo.form.advancedForm'),
-          },
-        },
-        {
-          path: 'ruleForm',
-          name: 'RuleFormDemo',
-          component: () => import('/@/views/demo/form/RuleForm.vue'),
-          meta: {
-            title: t('routes.demo.form.ruleForm'),
-          },
-        },
-        {
-          path: 'dynamicForm',
-          name: 'DynamicFormDemo',
-          component: () => import('/@/views/demo/form/DynamicForm.vue'),
-          meta: {
-            title: t('routes.demo.form.dynamicForm'),
-          },
-        },
-        {
-          path: 'customerForm',
-          name: 'CustomerFormDemo',
-          component: () => import('/@/views/demo/form/CustomerForm.vue'),
-          meta: {
-            title: t('routes.demo.form.customerForm'),
-          },
-        },
-        {
-          path: 'appendForm',
-          name: 'appendFormDemo',
-          component: () => import('/@/views/demo/form/AppendForm.vue'),
-          meta: {
-            title: t('routes.demo.form.appendForm'),
-          },
-        },
-        {
-          path: 'tabsForm',
-          name: 'tabsFormDemo',
-          component: () => import('/@/views/demo/form/TabsForm.vue'),
-          meta: {
-            title: t('routes.demo.form.tabsForm'),
-          },
-        },
-      ],
-    },
-    {
-      path: 'table',
-      name: 'TableDemo',
-      redirect: '/comp/table/basic',
-      component: getParentLayout('TableDemo'),
-      meta: {
-        // icon: 'carbon:table-split',
-        title: t('routes.demo.table.table'),
-      },
-
-      children: [
-        {
-          path: 'basic',
-          name: 'TableBasicDemo',
-          component: () => import('/@/views/demo/table/Basic.vue'),
-          meta: {
-            title: t('routes.demo.table.basic'),
-          },
-        },
-        {
-          path: 'treeTable',
-          name: 'TreeTableDemo',
-          component: () => import('/@/views/demo/table/TreeTable.vue'),
-          meta: {
-            title: t('routes.demo.table.treeTable'),
-          },
-        },
-        {
-          path: 'fetchTable',
-          name: 'FetchTableDemo',
-          component: () => import('/@/views/demo/table/FetchTable.vue'),
-          meta: {
-            title: t('routes.demo.table.fetchTable'),
-          },
-        },
-        {
-          path: 'fixedColumn',
-          name: 'FixedColumnDemo',
-          component: () => import('/@/views/demo/table/FixedColumn.vue'),
-          meta: {
-            title: t('routes.demo.table.fixedColumn'),
-          },
-        },
-        {
-          path: 'customerCell',
-          name: 'CustomerCellDemo',
-          component: () => import('/@/views/demo/table/CustomerCell.vue'),
-          meta: {
-            title: t('routes.demo.table.customerCell'),
-          },
-        },
-        {
-          path: 'formTable',
-          name: 'FormTableDemo',
-          component: () => import('/@/views/demo/table/FormTable.vue'),
-          meta: {
-            title: t('routes.demo.table.formTable'),
-          },
-        },
-        {
-          path: 'useTable',
-          name: 'UseTableDemo',
-          component: () => import('/@/views/demo/table/UseTable.vue'),
-          meta: {
-            title: t('routes.demo.table.useTable'),
-          },
-        },
-        {
-          path: 'refTable',
-          name: 'RefTableDemo',
-          component: () => import('/@/views/demo/table/RefTable.vue'),
-          meta: {
-            title: t('routes.demo.table.refTable'),
-          },
-        },
-        {
-          path: 'multipleHeader',
-          name: 'MultipleHeaderDemo',
-          component: () => import('/@/views/demo/table/MultipleHeader.vue'),
-          meta: {
-            title: t('routes.demo.table.multipleHeader'),
-          },
-        },
-        {
-          path: 'mergeHeader',
-          name: 'MergeHeaderDemo',
-          component: () => import('/@/views/demo/table/MergeHeader.vue'),
-          meta: {
-            title: t('routes.demo.table.mergeHeader'),
-          },
-        },
-        {
-          path: 'expandTable',
-          name: 'ExpandTableDemo',
-          component: () => import('/@/views/demo/table/ExpandTable.vue'),
-          meta: {
-            title: t('routes.demo.table.expandTable'),
-          },
-        },
-        {
-          path: 'fixedHeight',
-          name: 'FixedHeightDemo',
-          component: () => import('/@/views/demo/table/FixedHeight.vue'),
-          meta: {
-            title: t('routes.demo.table.fixedHeight'),
-          },
-        },
-        {
-          path: 'footerTable',
-          name: 'FooterTableDemo',
-          component: () => import('/@/views/demo/table/FooterTable.vue'),
-          meta: {
-            title: t('routes.demo.table.footerTable'),
-          },
-        },
-        {
-          path: 'editCellTable',
-          name: 'EditCellTableDemo',
-          component: () => import('/@/views/demo/table/EditCellTable.vue'),
-          meta: {
-            title: t('routes.demo.table.editCellTable'),
-          },
-        },
-        {
-          path: 'editRowTable',
-          name: 'EditRowTableDemo',
-          component: () => import('/@/views/demo/table/EditRowTable.vue'),
-          meta: {
-            title: t('routes.demo.table.editRowTable'),
-          },
-        },
-        {
-          path: 'authColumn',
-          name: 'AuthColumnDemo',
-          component: () => import('/@/views/demo/table/AuthColumn.vue'),
-          meta: {
-            title: t('routes.demo.table.authColumn'),
-          },
-        },
-        {
-          path: 'resizeParentHeightTable',
-          name: 'ResizeParentHeightTable',
-          component: () => import('/@/views/demo/table/ResizeParentHeightTable.vue'),
-          meta: {
-            title: t('routes.demo.table.resizeParentHeightTable'),
-          },
-        },
-      ],
-    },
-    {
-      path: 'transition',
-      name: 'transitionDemo',
-      component: () => import('/@/views/demo/comp/transition/index.vue'),
-      meta: {
-        title: t('routes.demo.comp.transition'),
-      },
-    },
-    {
-      path: 'cropper',
-      name: 'CropperDemo',
-      component: () => import('/@/views/demo/comp/cropper/index.vue'),
-      meta: {
-        title: t('routes.demo.comp.cropperImage'),
-      },
-    },
-
-    {
-      path: 'timestamp',
-      name: 'TimeDemo',
-      component: () => import('/@/views/demo/comp/time/index.vue'),
-      meta: {
-        title: t('routes.demo.comp.time'),
-      },
-    },
-    {
-      path: 'countTo',
-      name: 'CountTo',
-      component: () => import('/@/views/demo/comp/count-to/index.vue'),
-      meta: {
-        title: t('routes.demo.comp.countTo'),
-      },
-    },
-    {
-      path: 'tree',
-      name: 'TreeDemo',
-      redirect: '/comp/tree/basic',
-      component: getParentLayout('TreeDemo'),
-      meta: {
-        // icon: 'clarity:tree-view-line',
-        title: t('routes.demo.comp.tree'),
-      },
-      children: [
-        {
-          path: 'basic',
-          name: 'BasicTreeDemo',
-          component: () => import('/@/views/demo/tree/index.vue'),
-          meta: {
-            title: t('routes.demo.comp.treeBasic'),
-          },
-        },
-        {
-          path: 'editTree',
-          name: 'EditTreeDemo',
-          component: () => import('/@/views/demo/tree/EditTree.vue'),
-          meta: {
-            title: t('routes.demo.comp.editTree'),
-          },
-        },
-        {
-          path: 'actionTree',
-          name: 'ActionTreeDemo',
-          component: () => import('/@/views/demo/tree/ActionTree.vue'),
-          meta: {
-            title: t('routes.demo.comp.actionTree'),
-          },
-        },
-      ],
-    },
-    {
-      path: 'editor',
-      name: 'EditorDemo',
-      redirect: '/comp/editor/markdown',
-      component: getParentLayout('EditorDemo'),
-      meta: {
-        // icon: 'carbon:table-split',
-        title: t('routes.demo.editor.editor'),
-      },
-      children: [
-        {
-          path: 'json',
-          component: () => import('/@/views/demo/editor/json/index.vue'),
-          name: 'JsonEditorDemo',
-          meta: {
-            title: t('routes.demo.editor.jsonEditor'),
-          },
-        },
-        {
-          path: 'markdown',
-          component: getParentLayout('MarkdownDemo'),
-          name: 'MarkdownDemo',
-          meta: {
-            title: t('routes.demo.editor.markdown'),
-          },
-          redirect: '/comp/editor/markdown/index',
-          children: [
-            {
-              path: 'index',
-              name: 'MarkDownBasicDemo',
-              component: () => import('/@/views/demo/editor/markdown/index.vue'),
-              meta: {
-                title: t('routes.demo.editor.tinymceBasic'),
-              },
-            },
-            {
-              path: 'editor',
-              name: 'MarkDownFormDemo',
-              component: () => import('/@/views/demo/editor/markdown/Editor.vue'),
-              meta: {
-                title: t('routes.demo.editor.tinymceForm'),
-              },
-            },
-          ],
-        },
-
-        {
-          path: 'tinymce',
-          component: getParentLayout('TinymceDemo'),
-          name: 'TinymceDemo',
-          meta: {
-            title: t('routes.demo.editor.tinymce'),
-          },
-          redirect: '/comp/editor/tinymce/index',
-          children: [
-            {
-              path: 'index',
-              name: 'TinymceBasicDemo',
-              component: () => import('/@/views/demo/editor/tinymce/index.vue'),
-              meta: {
-                title: t('routes.demo.editor.tinymceBasic'),
-              },
-            },
-            {
-              path: 'editor',
-              name: 'TinymceFormDemo',
-              component: () => import('/@/views/demo/editor/tinymce/Editor.vue'),
-              meta: {
-                title: t('routes.demo.editor.tinymceForm'),
-              },
-            },
-          ],
-        },
-      ],
-    },
-    {
-      path: 'scroll',
-      name: 'ScrollDemo',
-      redirect: '/comp/scroll/basic',
-      component: getParentLayout('ScrollDemo'),
-      meta: {
-        title: t('routes.demo.comp.scroll'),
-      },
-      children: [
-        {
-          path: 'basic',
-          name: 'BasicScrollDemo',
-          component: () => import('/@/views/demo/comp/scroll/index.vue'),
-          meta: {
-            title: t('routes.demo.comp.scrollBasic'),
-          },
-        },
-        {
-          path: 'action',
-          name: 'ActionScrollDemo',
-          component: () => import('/@/views/demo/comp/scroll/Action.vue'),
-          meta: {
-            title: t('routes.demo.comp.scrollAction'),
-          },
-        },
-        {
-          path: 'virtualScroll',
-          name: 'VirtualScrollDemo',
-          component: () => import('/@/views/demo/comp/scroll/VirtualScroll.vue'),
-          meta: {
-            title: t('routes.demo.comp.virtualScroll'),
-          },
-        },
-      ],
-    },
-
-    {
-      path: 'modal',
-      name: 'ModalDemo',
-      component: () => import('/@/views/demo/comp/modal/index.vue'),
-      meta: {
-        title: t('routes.demo.comp.modal'),
-      },
-    },
-    {
-      path: 'drawer',
-      name: 'DrawerDemo',
-      component: () => import('/@/views/demo/comp/drawer/index.vue'),
-      meta: {
-        title: t('routes.demo.comp.drawer'),
-      },
-    },
-    {
-      path: 'desc',
-      name: 'DescDemo',
-      component: () => import('/@/views/demo/comp/desc/index.vue'),
-      meta: {
-        title: t('routes.demo.comp.desc'),
-      },
-    },
-
-    {
-      path: 'lazy',
-      name: 'LazyDemo',
-      component: getParentLayout('LazyDemo'),
-      redirect: '/comp/lazy/basic',
-      meta: {
-        title: t('routes.demo.comp.lazy'),
-      },
-      children: [
-        {
-          path: 'basic',
-          name: 'BasicLazyDemo',
-          component: () => import('/@/views/demo/comp/lazy/index.vue'),
-          meta: {
-            title: t('routes.demo.comp.lazyBasic'),
-          },
-        },
-        {
-          path: 'transition',
-          name: 'BasicTransitionDemo',
-          component: () => import('/@/views/demo/comp/lazy/Transition.vue'),
-          meta: {
-            title: t('routes.demo.comp.lazyTransition'),
-          },
-        },
-      ],
-    },
-    {
-      path: 'verify',
-      name: 'VerifyDemo',
-      component: getParentLayout('VerifyDemo'),
-      redirect: '/comp/verify/drag',
-      meta: {
-        title: t('routes.demo.comp.verify'),
-      },
-      children: [
-        {
-          path: 'drag',
-          name: 'VerifyDragDemo',
-          component: () => import('/@/views/demo/comp/verify/index.vue'),
-          meta: {
-            title: t('routes.demo.comp.verifyDrag'),
-          },
-        },
-        {
-          path: 'rotate',
-          name: 'VerifyRotateDemo',
-          component: () => import('/@/views/demo/comp/verify/Rotate.vue'),
-          meta: {
-            title: t('routes.demo.comp.verifyRotate'),
-          },
-        },
-      ],
-    },
-    //
-
-    {
-      path: 'qrcode',
-      name: 'QrCodeDemo',
-      component: () => import('/@/views/demo/comp/qrcode/index.vue'),
-      meta: {
-        title: t('routes.demo.comp.qrcode'),
-      },
-    },
-    {
-      path: 'strength-meter',
-      name: 'StrengthMeterDemo',
-      component: () => import('/@/views/demo/comp/strength-meter/index.vue'),
-      meta: {
-        title: t('routes.demo.comp.strength'),
-      },
-    },
-    {
-      path: 'upload',
-      name: 'UploadDemo',
-      component: () => import('/@/views/demo/comp/upload/index.vue'),
-      meta: {
-        title: t('routes.demo.comp.upload'),
-      },
-    },
-    {
-      path: 'loading',
-      name: 'LoadingDemo',
-      component: () => import('/@/views/demo/comp/loading/index.vue'),
-      meta: {
-        title: t('routes.demo.comp.loading'),
-      },
-    },
-    {
-      path: 'cardList',
-      name: 'CardListDemo',
-      component: () => import('/@/views/demo/comp/card-list/index.vue'),
-      meta: {
-        title: t('routes.demo.comp.cardList'),
-      },
-    },
-  ],
-};
-
-export default comp;

+ 0 - 324
src/router/routes/modules/demo/feat.ts

@@ -1,324 +0,0 @@
-import type { AppRouteModule } from '/@/router/types';
-
-import { getParentLayout, LAYOUT } from '/@/router/constant';
-import { t } from '/@/hooks/web/useI18n';
-
-const feat: AppRouteModule = {
-  path: '/feat',
-  name: 'FeatDemo',
-  component: LAYOUT,
-  redirect: '/feat/icon',
-  meta: {
-    orderNo: 19,
-    icon: 'ion:git-compare-outline',
-    title: t('routes.demo.feat.feat'),
-  },
-
-  children: [
-    {
-      path: 'icon',
-      name: 'IconDemo',
-      component: () => import('/@/views/demo/feat/icon/index.vue'),
-      meta: {
-        title: t('routes.demo.feat.icon'),
-      },
-    },
-    {
-      path: 'ws',
-      name: 'WebSocket',
-      component: () => import('/@/views/demo/feat/ws/index.vue'),
-      meta: {
-        title: t('routes.demo.feat.ws'),
-      },
-    },
-    {
-      path: 'request',
-      name: 'RequestDemo',
-      // @ts-ignore
-      component: () => import('/@/views/demo/feat/request-demo/index.vue'),
-      meta: {
-        title: t('routes.demo.feat.requestDemo'),
-      },
-    },
-    {
-      path: 'session-timeout',
-      name: 'SessionTimeout',
-      component: () => import('/@/views/demo/feat/session-timeout/index.vue'),
-      meta: {
-        title: t('routes.demo.feat.sessionTimeout'),
-      },
-    },
-    {
-      path: 'print',
-      name: 'Print',
-      component: () => import('/@/views/demo/feat/print/index.vue'),
-      meta: {
-        title: t('routes.demo.feat.print'),
-      },
-    },
-    {
-      path: 'tabs',
-      name: 'TabsDemo',
-      component: () => import('/@/views/demo/feat/tabs/index.vue'),
-      meta: {
-        title: t('routes.demo.feat.tabs'),
-        hideChildrenInMenu: true,
-      },
-      children: [
-        {
-          path: 'detail/:id',
-          name: 'TabDetail',
-          component: () => import('/@/views/demo/feat/tabs/TabDetail.vue'),
-          meta: {
-            currentActiveMenu: '/feat/tabs',
-            title: t('routes.demo.feat.tabDetail'),
-            hideMenu: true,
-            dynamicLevel: 3,
-            realPath: '/feat/tabs/detail',
-          },
-        },
-      ],
-    },
-    {
-      path: 'breadcrumb',
-      name: 'BreadcrumbDemo',
-      redirect: '/feat/breadcrumb/flat',
-      component: getParentLayout('BreadcrumbDemo'),
-      meta: {
-        title: t('routes.demo.feat.breadcrumb'),
-      },
-
-      children: [
-        {
-          path: 'flat',
-          name: 'BreadcrumbFlatDemo',
-          component: () => import('/@/views/demo/feat/breadcrumb/FlatList.vue'),
-          meta: {
-            title: t('routes.demo.feat.breadcrumbFlat'),
-          },
-        },
-        {
-          path: 'flatDetail',
-          name: 'BreadcrumbFlatDetailDemo',
-          component: () => import('/@/views/demo/feat/breadcrumb/FlatListDetail.vue'),
-          meta: {
-            title: t('routes.demo.feat.breadcrumbFlatDetail'),
-            hideMenu: true,
-            hideTab: true,
-            currentActiveMenu: '/feat/breadcrumb/flat',
-          },
-        },
-        {
-          path: 'children',
-          name: 'BreadcrumbChildrenDemo',
-          component: () => import('/@/views/demo/feat/breadcrumb/ChildrenList.vue'),
-          meta: {
-            title: t('routes.demo.feat.breadcrumbChildren'),
-          },
-          children: [
-            {
-              path: 'childrenDetail',
-              name: 'BreadcrumbChildrenDetailDemo',
-              component: () => import('/@/views/demo/feat/breadcrumb/ChildrenListDetail.vue'),
-              meta: {
-                currentActiveMenu: '/feat/breadcrumb/children',
-                title: t('routes.demo.feat.breadcrumbChildrenDetail'),
-                //hideTab: true,
-                // hideMenu: true,
-              },
-            },
-          ],
-        },
-      ],
-    },
-
-    {
-      path: 'context-menu',
-      name: 'ContextMenuDemo',
-      component: () => import('/@/views/demo/feat/context-menu/index.vue'),
-      meta: {
-        title: t('routes.demo.feat.contextMenu'),
-      },
-    },
-    {
-      path: 'download',
-      name: 'DownLoadDemo',
-      component: () => import('/@/views/demo/feat/download/index.vue'),
-      meta: {
-        title: t('routes.demo.feat.download'),
-      },
-    },
-    {
-      path: 'click-out-side',
-      name: 'ClickOutSideDemo',
-      component: () => import('/@/views/demo/feat/click-out-side/index.vue'),
-      meta: {
-        title: t('routes.demo.feat.clickOutSide'),
-      },
-    },
-    {
-      path: 'img-preview',
-      name: 'ImgPreview',
-      component: () => import('/@/views/demo/feat/img-preview/index.vue'),
-      meta: {
-        title: t('routes.demo.feat.imgPreview'),
-      },
-    },
-    {
-      path: 'copy',
-      name: 'CopyDemo',
-      component: () => import('/@/views/demo/feat/copy/index.vue'),
-      meta: {
-        title: t('routes.demo.feat.copy'),
-      },
-    },
-    {
-      path: 'msg',
-      name: 'MsgDemo',
-      component: () => import('/@/views/demo/feat/msg/index.vue'),
-      meta: {
-        title: t('routes.demo.feat.msg'),
-      },
-    },
-    {
-      path: 'watermark',
-      name: 'WatermarkDemo',
-      component: () => import('/@/views/demo/feat/watermark/index.vue'),
-      meta: {
-        title: t('routes.demo.feat.watermark'),
-      },
-    },
-    {
-      path: 'ripple',
-      name: 'RippleDemo',
-      component: () => import('/@/views/demo/feat/ripple/index.vue'),
-      meta: {
-        title: t('routes.demo.feat.ripple'),
-      },
-    },
-    {
-      path: 'full-screen',
-      name: 'FullScreenDemo',
-      component: () => import('/@/views/demo/feat/full-screen/index.vue'),
-      meta: {
-        title: t('routes.demo.feat.fullScreen'),
-      },
-    },
-    {
-      path: '/error-log',
-      name: 'ErrorLog',
-      component: () => import('/@/views/sys/error-log/index.vue'),
-      meta: {
-        title: t('routes.demo.feat.errorLog'),
-      },
-    },
-    {
-      path: 'excel',
-      name: 'Excel',
-      redirect: '/feat/excel/customExport',
-      component: getParentLayout('Excel'),
-      meta: {
-        // icon: 'mdi:microsoft-excel',
-        title: t('routes.demo.excel.excel'),
-      },
-
-      children: [
-        {
-          path: 'customExport',
-          name: 'CustomExport',
-          component: () => import('/@/views/demo/excel/CustomExport.vue'),
-          meta: {
-            title: t('routes.demo.excel.customExport'),
-          },
-        },
-        {
-          path: 'jsonExport',
-          name: 'JsonExport',
-          component: () => import('/@/views/demo/excel/JsonExport.vue'),
-          meta: {
-            title: t('routes.demo.excel.jsonExport'),
-          },
-        },
-        {
-          path: 'arrayExport',
-          name: 'ArrayExport',
-          component: () => import('/@/views/demo/excel/ArrayExport.vue'),
-          meta: {
-            title: t('routes.demo.excel.arrayExport'),
-          },
-        },
-        {
-          path: 'importExcel',
-          name: 'ImportExcel',
-          component: () => import('/@/views/demo/excel/ImportExcel.vue'),
-          meta: {
-            title: t('routes.demo.excel.importExcel'),
-          },
-        },
-      ],
-    },
-    {
-      path: 'testTab/:id',
-      name: 'TestTab',
-      component: () => import('/@/views/demo/feat/tab-params/index.vue'),
-      meta: {
-        title: t('routes.demo.feat.tab'),
-        carryParam: true,
-        hidePathForChildren: true,
-      },
-      children: [
-        {
-          path: 'testTab/id1',
-          name: 'TestTab1',
-          component: () => import('/@/views/demo/feat/tab-params/index.vue'),
-          meta: {
-            title: t('routes.demo.feat.tab1'),
-            carryParam: true,
-            ignoreRoute: true,
-          },
-        },
-        {
-          path: 'testTab/id2',
-          name: 'TestTab2',
-          component: () => import('/@/views/demo/feat/tab-params/index.vue'),
-          meta: {
-            title: t('routes.demo.feat.tab2'),
-            carryParam: true,
-            ignoreRoute: true,
-          },
-        },
-      ],
-    },
-    {
-      path: 'testParam/:id',
-      name: 'TestParam',
-      component: getParentLayout('TestParam'),
-      meta: {
-        title: t('routes.demo.feat.menu'),
-        ignoreKeepAlive: true,
-      },
-      children: [
-        {
-          path: 'sub1',
-          name: 'TestParam_1',
-          component: () => import('/@/views/demo/feat/menu-params/index.vue'),
-          meta: {
-            title: t('routes.demo.feat.menu1'),
-            ignoreKeepAlive: true,
-          },
-        },
-        {
-          path: 'sub2',
-          name: 'TestParam_2',
-          component: () => import('/@/views/demo/feat/menu-params/index.vue'),
-          meta: {
-            title: t('routes.demo.feat.menu2'),
-            ignoreKeepAlive: true,
-          },
-        },
-      ],
-    },
-  ],
-};
-
-export default feat;

+ 0 - 28
src/router/routes/modules/demo/flow.ts

@@ -1,28 +0,0 @@
-import type { AppRouteModule } from '/@/router/types';
-
-import { LAYOUT } from '/@/router/constant';
-import { t } from '/@/hooks/web/useI18n';
-
-const charts: AppRouteModule = {
-  path: '/flow',
-  name: 'FlowDemo',
-  component: LAYOUT,
-  redirect: '/flow/flowChart',
-  meta: {
-    orderNo: 5000,
-    icon: 'tabler:chart-dots',
-    title: t('routes.demo.flow.name'),
-  },
-  children: [
-    {
-      path: 'flowChart',
-      name: 'flowChartDemo',
-      component: () => import('/@/views/demo/comp/flow-chart/index.vue'),
-      meta: {
-        title: t('routes.demo.flow.flowChart'),
-      },
-    },
-  ],
-};
-
-export default charts;

+ 0 - 48
src/router/routes/modules/demo/iframe.ts

@@ -1,48 +0,0 @@
-import type { AppRouteModule } from '/@/router/types';
-
-import { LAYOUT } from '/@/router/constant';
-const IFrame = () => import('/@/views/sys/iframe/FrameBlank.vue');
-import { t } from '/@/hooks/web/useI18n';
-
-const iframe: AppRouteModule = {
-  path: '/frame',
-  name: 'Frame',
-  component: LAYOUT,
-  redirect: '/frame/doc',
-  meta: {
-    orderNo: 1000,
-    icon: 'ion:tv-outline',
-    title: t('routes.demo.iframe.frame'),
-  },
-
-  children: [
-    {
-      path: 'doc',
-      name: 'Doc',
-      component: IFrame,
-      meta: {
-        frameSrc: 'https://vvbin.cn/doc-next/',
-        title: t('routes.demo.iframe.doc'),
-      },
-    },
-    {
-      path: 'antv',
-      name: 'Antv',
-      component: IFrame,
-      meta: {
-        frameSrc: 'https://2x.antdv.com/docs/vue/introduce-cn/',
-        title: t('routes.demo.iframe.antv'),
-      },
-    },
-    {
-      path: 'https://vvbin.cn/doc-next/',
-      name: 'DocExternal',
-      component: IFrame,
-      meta: {
-        title: t('routes.demo.iframe.docExternal'),
-      },
-    },
-  ],
-};
-
-export default iframe;

+ 0 - 68
src/router/routes/modules/demo/level.ts

@@ -1,68 +0,0 @@
-import type { AppRouteModule } from '/@/router/types';
-
-import { getParentLayout, LAYOUT } from '/@/router/constant';
-import { t } from '/@/hooks/web/useI18n';
-
-const permission: AppRouteModule = {
-  path: '/level',
-  name: 'Level',
-  component: LAYOUT,
-  redirect: '/level/menu1/menu1-1/menu1-1-1',
-  meta: {
-    orderNo: 2000,
-    icon: 'ion:menu-outline',
-    title: t('routes.demo.level.level'),
-  },
-
-  children: [
-    {
-      path: 'menu1',
-      name: 'Menu1Demo',
-      component: getParentLayout('Menu1Demo'),
-      meta: {
-        title: 'Menu1',
-      },
-      redirect: '/level/menu1/menu1-1/menu1-1-1',
-      children: [
-        {
-          path: 'menu1-1',
-          name: 'Menu11Demo',
-          component: getParentLayout('Menu11Demo'),
-          meta: {
-            title: 'Menu1-1',
-          },
-          redirect: '/level/menu1/menu1-1/menu1-1-1',
-          children: [
-            {
-              path: 'menu1-1-1',
-              name: 'Menu111Demo',
-              component: () => import('/@/views/demo/level/Menu111.vue'),
-              meta: {
-                title: 'Menu111',
-              },
-            },
-          ],
-        },
-        {
-          path: 'menu1-2',
-          name: 'Menu12Demo',
-          component: () => import('/@/views/demo/level/Menu12.vue'),
-          meta: {
-            title: 'Menu1-2',
-          },
-        },
-      ],
-    },
-    {
-      path: 'menu2',
-      name: 'Menu2Demo',
-      component: () => import('/@/views/demo/level/Menu2.vue'),
-      meta: {
-        title: 'Menu2',
-        // ignoreKeepAlive: true,
-      },
-    },
-  ],
-};
-
-export default permission;

+ 0 - 255
src/router/routes/modules/demo/page.ts

@@ -1,255 +0,0 @@
-import type { AppRouteModule } from '/@/router/types';
-
-import { getParentLayout, LAYOUT } from '/@/router/constant';
-import { ExceptionEnum } from '/@/enums/exceptionEnum';
-import { t } from '/@/hooks/web/useI18n';
-
-const ExceptionPage = () => import('/@/views/sys/exception/Exception.vue');
-
-const page: AppRouteModule = {
-  path: '/page-demo',
-  name: 'PageDemo',
-  component: LAYOUT,
-  redirect: '/page-demo/form/basic',
-  meta: {
-    orderNo: 20,
-    icon: 'ion:aperture-outline',
-    title: t('routes.demo.page.page'),
-  },
-  children: [
-    // =============================form start=============================
-    {
-      path: 'form',
-      name: 'FormPage',
-      redirect: '/page-demo/form/basic',
-      component: getParentLayout('FormPage'),
-      meta: {
-        title: t('routes.demo.page.form'),
-      },
-      children: [
-        {
-          path: 'basic',
-          name: 'FormBasicPage',
-          component: () => import('/@/views/demo/page/form/basic/index.vue'),
-          meta: {
-            title: t('routes.demo.page.formBasic'),
-          },
-        },
-        {
-          path: 'step',
-          name: 'FormStepPage',
-          component: () => import('/@/views/demo/page/form/step/index.vue'),
-          meta: {
-            title: t('routes.demo.page.formStep'),
-          },
-        },
-        {
-          path: 'high',
-          name: 'FormHightPage',
-          component: () => import('/@/views/demo/page/form/high/index.vue'),
-          meta: {
-            title: t('routes.demo.page.formHigh'),
-          },
-        },
-      ],
-    },
-    // =============================form end=============================
-    // =============================desc start=============================
-    {
-      path: 'desc',
-      name: 'DescPage',
-      component: getParentLayout('DescPage'),
-      redirect: '/page-demo/desc/basic',
-      meta: {
-        title: t('routes.demo.page.desc'),
-      },
-      children: [
-        {
-          path: 'basic',
-          name: 'DescBasicPage',
-          component: () => import('/@/views/demo/page/desc/basic/index.vue'),
-          meta: {
-            title: t('routes.demo.page.descBasic'),
-          },
-        },
-        {
-          path: 'high',
-          name: 'DescHighPage',
-          component: () => import('/@/views/demo/page/desc/high/index.vue'),
-          meta: {
-            title: t('routes.demo.page.descHigh'),
-          },
-        },
-      ],
-    },
-    // =============================desc end=============================
-
-    // =============================result start=============================
-    {
-      path: 'result',
-      name: 'ResultPage',
-      redirect: '/page-demo/result/success',
-      component: getParentLayout('ResultPage'),
-
-      meta: {
-        title: t('routes.demo.page.result'),
-      },
-      children: [
-        {
-          path: 'success',
-          name: 'ResultSuccessPage',
-          component: () => import('/@/views/demo/page/result/success/index.vue'),
-          meta: {
-            title: t('routes.demo.page.resultSuccess'),
-          },
-        },
-        {
-          path: 'fail',
-          name: 'ResultFailPage',
-          component: () => import('/@/views/demo/page/result/fail/index.vue'),
-          meta: {
-            title: t('routes.demo.page.resultFail'),
-          },
-        },
-      ],
-    },
-    // =============================result end=============================
-
-    // =============================account start=============================
-    {
-      path: 'account',
-      name: 'AccountPage',
-      component: getParentLayout('AccountPage'),
-      redirect: '/page-demo/account/setting',
-      meta: {
-        title: t('routes.demo.page.account'),
-      },
-      children: [
-        {
-          path: 'center',
-          name: 'AccountCenterPage',
-          component: () => import('/@/views/demo/page/account/center/index.vue'),
-          meta: {
-            title: t('routes.demo.page.accountCenter'),
-          },
-        },
-        {
-          path: 'setting',
-          name: 'AccountSettingPage',
-          component: () => import('/@/views/demo/page/account/setting/index.vue'),
-          meta: {
-            title: t('routes.demo.page.accountSetting'),
-          },
-        },
-      ],
-    },
-    // =============================account end=============================
-    // =============================exception start=============================
-    {
-      path: 'exception',
-      name: 'ExceptionPage',
-      component: getParentLayout('ExceptionPage'),
-      redirect: '/page-demo/exception/404',
-      meta: {
-        title: t('routes.demo.page.exception'),
-      },
-      children: [
-        {
-          path: '403',
-          name: 'PageNotAccess',
-          component: ExceptionPage,
-          props: {
-            status: ExceptionEnum.PAGE_NOT_ACCESS,
-          },
-          meta: {
-            title: '403',
-          },
-        },
-        {
-          path: '404',
-          name: 'PageNotFound',
-          component: ExceptionPage,
-          props: {
-            status: ExceptionEnum.PAGE_NOT_FOUND,
-          },
-          meta: {
-            title: '404',
-          },
-        },
-        {
-          path: '500',
-          name: 'ServiceError',
-          component: ExceptionPage,
-          props: {
-            status: ExceptionEnum.ERROR,
-          },
-          meta: {
-            title: '500',
-          },
-        },
-        {
-          path: 'net-work-error',
-          name: 'NetWorkError',
-          component: ExceptionPage,
-          props: {
-            status: ExceptionEnum.NET_WORK_ERROR,
-          },
-          meta: {
-            title: t('routes.demo.page.netWorkError'),
-          },
-        },
-        {
-          path: 'not-data',
-          name: 'NotData',
-          component: ExceptionPage,
-          props: {
-            status: ExceptionEnum.PAGE_NOT_DATA,
-          },
-          meta: {
-            title: t('routes.demo.page.notData'),
-          },
-        },
-      ],
-    },
-    // =============================exception end=============================
-    // =============================list start=============================
-    {
-      path: 'list',
-      name: 'ListPage',
-      component: getParentLayout('ListPage'),
-      redirect: '/page-demo/list/card',
-      meta: {
-        title: t('routes.demo.page.list'),
-      },
-      children: [
-        {
-          path: 'basic',
-          name: 'ListBasicPage',
-          component: () => import('/@/views/demo/page/list/basic/index.vue'),
-          meta: {
-            title: t('routes.demo.page.listBasic'),
-          },
-        },
-        {
-          path: 'card',
-          name: 'ListCardPage',
-          component: () => import('/@/views/demo/page/list/card/index.vue'),
-          meta: {
-            title: t('routes.demo.page.listCard'),
-          },
-        },
-        {
-          path: 'search',
-          name: 'ListSearchPage',
-          component: () => import('/@/views/demo/page/list/search/index.vue'),
-          meta: {
-            title: t('routes.demo.page.listSearch'),
-          },
-        },
-      ],
-    },
-    // =============================list end=============================
-  ],
-};
-
-export default page;

+ 0 - 92
src/router/routes/modules/demo/permission.ts

@@ -1,92 +0,0 @@
-import type { AppRouteModule } from '/@/router/types';
-
-import { getParentLayout, LAYOUT } from '/@/router/constant';
-import { RoleEnum } from '/@/enums/roleEnum';
-import { t } from '/@/hooks/web/useI18n';
-
-const permission: AppRouteModule = {
-  path: '/permission',
-  name: 'Permission',
-  component: LAYOUT,
-  redirect: '/permission/front/page',
-  meta: {
-    orderNo: 15,
-    icon: 'ion:key-outline',
-    title: t('routes.demo.permission.permission'),
-  },
-
-  children: [
-    {
-      path: 'front',
-      name: 'PermissionFrontDemo',
-      component: getParentLayout('PermissionFrontDemo'),
-      meta: {
-        title: t('routes.demo.permission.front'),
-      },
-      children: [
-        {
-          path: 'page',
-          name: 'FrontPageAuth',
-          component: () => import('/@/views/demo/permission/front/index.vue'),
-          meta: {
-            title: t('routes.demo.permission.frontPage'),
-          },
-        },
-        {
-          path: 'btn',
-          name: 'FrontBtnAuth',
-          component: () => import('/@/views/demo/permission/front/Btn.vue'),
-          meta: {
-            title: t('routes.demo.permission.frontBtn'),
-          },
-        },
-        {
-          path: 'auth-pageA',
-          name: 'FrontAuthPageA',
-          component: () => import('/@/views/demo/permission/front/AuthPageA.vue'),
-          meta: {
-            title: t('routes.demo.permission.frontTestA'),
-            roles: [RoleEnum.SUPER],
-          },
-        },
-        {
-          path: 'auth-pageB',
-          name: 'FrontAuthPageB',
-          component: () => import('/@/views/demo/permission/front/AuthPageB.vue'),
-          meta: {
-            title: t('routes.demo.permission.frontTestB'),
-            roles: [RoleEnum.TEST],
-          },
-        },
-      ],
-    },
-    {
-      path: 'back',
-      name: 'PermissionBackDemo',
-      component: getParentLayout('PermissionBackDemo'),
-      meta: {
-        title: t('routes.demo.permission.back'),
-      },
-      children: [
-        {
-          path: 'page',
-          name: 'BackAuthPage',
-          component: () => import('/@/views/demo/permission/back/index.vue'),
-          meta: {
-            title: t('routes.demo.permission.backPage'),
-          },
-        },
-        {
-          path: 'btn',
-          name: 'BackAuthBtn',
-          component: () => import('/@/views/demo/permission/back/Btn.vue'),
-          meta: {
-            title: t('routes.demo.permission.backBtn'),
-          },
-        },
-      ],
-    },
-  ],
-};
-
-export default permission;

+ 0 - 31
src/router/routes/modules/demo/setup.ts

@@ -1,31 +0,0 @@
-import type { AppRouteModule } from '/@/router/types';
-
-import { LAYOUT } from '/@/router/constant';
-import { t } from '/@/hooks/web/useI18n';
-
-const setup: AppRouteModule = {
-  path: '/setup',
-  name: 'SetupDemo',
-  component: LAYOUT,
-  redirect: '/setup/index',
-  meta: {
-    orderNo: 90000,
-    hideChildrenInMenu: true,
-    icon: 'whh:paintroll',
-    title: t('routes.demo.setup.page'),
-  },
-  children: [
-    {
-      path: 'index',
-      name: 'SetupDemoPage',
-      component: () => import('/@/views/demo/setup/index.vue'),
-      meta: {
-        title: t('routes.demo.setup.page'),
-        icon: 'whh:paintroll',
-        hideMenu: true,
-      },
-    },
-  ],
-};
-
-export default setup;

+ 53 - 0
src/router/routes/modules/operate.ts

@@ -0,0 +1,53 @@
+import type { AppRouteModule } from '/@/router/types';
+
+import { LAYOUT } from '/@/router/constant';
+import { t } from '/@/hooks/web/useI18n';
+
+const order: AppRouteModule = {
+  path: '/operate',
+  name: 'Operate',
+  component: LAYOUT,
+  redirect: '/operate/news',
+  meta: {
+    icon: 'bi:columns',
+    title: t('routes.dashboard.operate'),
+    orderNo: 102,
+  },
+  children: [
+    {
+      path: 'news',
+      name: 'News',
+      component: () => import('/@/views/operate/newsList.vue'),
+      // component: () => import('/@/views/system/account/index.vue'),
+        meta: {
+        title: t('routes.dashboard.operateNews'),
+        icon: 'carbon:notification-new',
+        hideChildrenInMenu: true,
+      },
+    },
+    {
+      path: 'recruit',
+      name: 'Recruit',
+      component: () => import('/@/views/operate/recruitList.vue'),
+      // component: () => import('/@/views/system/account/index.vue'),
+        meta: {
+        title: t('routes.dashboard.operateRecruit'),
+        icon: 'ci:add-to-queue',
+        hideChildrenInMenu: true,
+      },
+    },
+    {
+      path: 'message',
+      name: 'Message',
+      component: () => import('/@/views/operate/messageList.vue'),
+      // component: () => import('/@/views/system/account/index.vue'),
+        meta: {
+        title: t('routes.dashboard.operateMessage'),
+        icon: 'bx:list-ul',
+        hideChildrenInMenu: true,
+      },
+    },
+  ],
+};
+
+export default order;

+ 93 - 0
src/router/routes/modules/order.ts

@@ -0,0 +1,93 @@
+import type { AppRouteModule } from '/@/router/types';
+
+import { LAYOUT } from '/@/router/constant';
+import { t } from '/@/hooks/web/useI18n';
+
+const order: AppRouteModule = {
+  path: '/order',
+  name: 'Order',
+  component: LAYOUT,
+  redirect: '/order/cameraList',
+  meta: {
+    icon: 'bi:columns',
+    title: t('routes.dashboard.order'),
+    orderNo: 102,
+  },
+  children: [
+    {
+      path: 'cameraList',
+      name: 'CameraList',
+      component: () => import('/@/views/order/cameraList.vue'),
+      // component: () => import('/@/views/system/account/index.vue'),
+        meta: {
+        title: t('routes.dashboard.cameraList'),
+        icon: 'bx:list-ul',
+        hideChildrenInMenu: true,
+      },
+      children: [
+        {
+          path: 'cameraDetail/:id',
+          name: 'CameraDetail',
+          component: () => import('/@/views/order/cameraDetail.vue'),
+          meta: {
+            currentActiveMenu: '/order/list',
+            title: t('routes.dashboard.orderDetail'),
+            hideMenu: true,
+            dynamicLevel: 3,
+            realPath: '/order/cameraList/cameraDetail',
+          },
+        },
+      ],
+    },
+    {
+      path: 'equityList',
+      name: 'EquityList',
+      component: () => import('/@/views/order/equityList.vue'),
+      meta: {
+        title: t('routes.dashboard.equityList'),
+        icon: 'icon-park-outline:light-member',
+        hideChildrenInMenu: true,
+      },
+      children: [
+        {
+          path: 'equityDetail/:id',
+          name: 'EquityDetail',
+          component: () => import('/@/views/order/equityDetail.vue'),
+          meta: {
+            currentActiveMenu: '/order/list',
+            title: t('routes.dashboard.orderDetail'),
+            hideMenu: true,
+            dynamicLevel: 3,
+            realPath: '/order/equityList/equityDetail',
+          },
+        },
+      ],
+    },
+    {
+      path: 'downloadList',
+      name: 'DownloadList',
+      component: () => import('/@/views/order/downloadList.vue'),
+      meta: {
+        title: t('routes.dashboard.downloadList'),
+        icon: 'ant-design:download-outlined',
+        hideChildrenInMenu: true,
+      },
+      children: [
+        {
+          path: 'downloadDetail/:id',
+          name: 'DownloadDetail',
+          component: () => import('/@/views/order/downloadDetail.vue'),
+          meta: {
+            currentActiveMenu: '/order/list',
+            title: t('routes.dashboard.orderDetail'),
+            hideMenu: true,
+            dynamicLevel: 3,
+            realPath: '/order/equityList/downloadDetail',
+          },
+        },
+      ],
+    },
+  ],
+};
+
+export default order;

+ 6 - 6
src/router/routes/modules/demo/system.ts

@@ -21,7 +21,7 @@ const system: AppRouteModule = {
         title: t('routes.demo.system.account'),
         ignoreKeepAlive: false,
       },
-      component: () => import('/@/views/demo/system/account/index.vue'),
+      component: () => import('/@/views/system/account/index.vue'),
     },
     {
       path: 'account_detail/:id',
@@ -33,7 +33,7 @@ const system: AppRouteModule = {
         showMenu: false,
         currentActiveMenu: '/system/account',
       },
-      component: () => import('/@/views/demo/system/account/AccountDetail.vue'),
+      component: () => import('/@/views/system/account/AccountDetail.vue'),
     },
     {
       path: 'role',
@@ -42,7 +42,7 @@ const system: AppRouteModule = {
         title: t('routes.demo.system.role'),
         ignoreKeepAlive: true,
       },
-      component: () => import('/@/views/demo/system/role/index.vue'),
+      component: () => import('/@/views/system/role/index.vue'),
     },
 
     {
@@ -52,7 +52,7 @@ const system: AppRouteModule = {
         title: t('routes.demo.system.menu'),
         ignoreKeepAlive: true,
       },
-      component: () => import('/@/views/demo/system/menu/index.vue'),
+      component: () => import('/@/views/system/menu/index.vue'),
     },
     {
       path: 'dept',
@@ -61,7 +61,7 @@ const system: AppRouteModule = {
         title: t('routes.demo.system.dept'),
         ignoreKeepAlive: true,
       },
-      component: () => import('/@/views/demo/system/dept/index.vue'),
+      component: () => import('/@/views/system/dept/index.vue'),
     },
     {
       path: 'changePassword',
@@ -70,7 +70,7 @@ const system: AppRouteModule = {
         title: t('routes.demo.system.password'),
         ignoreKeepAlive: true,
       },
-      component: () => import('/@/views/demo/system/password/index.vue'),
+      component: () => import('/@/views/system/password/index.vue'),
     },
   ],
 };

+ 12 - 9
src/store/modules/user.ts

@@ -1,4 +1,4 @@
-import type { UserInfo } from '/#/store';
+import type { UserInfoV4 } from '/#/store';
 import type { ErrorMessageMode } from '/#/axios';
 import { defineStore } from 'pinia';
 import { store } from '/@/store';
@@ -18,7 +18,7 @@ import { isArray } from '/@/utils/is';
 import { h } from 'vue';
 
 interface UserState {
-  userInfo: Nullable<UserInfo>;
+  userInfo: Nullable<UserInfoV4>;
   token?: string;
   roleList: RoleEnum[];
   sessionTimeout?: boolean;
@@ -40,8 +40,8 @@ export const useUserStore = defineStore({
     lastUpdateTime: 0,
   }),
   getters: {
-    getUserInfo(): UserInfo {
-      return this.userInfo || getAuthCache<UserInfo>(USER_INFO_KEY) || {};
+    getUserInfo(): UserInfoV4 {
+      return this.userInfo || getAuthCache<UserInfoV4>(USER_INFO_KEY) || {};
     },
     getToken(): string {
       return this.token || getAuthCache<string>(TOKEN_KEY);
@@ -65,7 +65,7 @@ export const useUserStore = defineStore({
       this.roleList = roleList;
       setAuthCache(ROLES_KEY, roleList);
     },
-    setUserInfo(info: UserInfo | null) {
+    setUserInfo(info: UserInfoV4 | null) {
       this.userInfo = info;
       this.lastUpdateTime = new Date().getTime();
       setAuthCache(USER_INFO_KEY, info);
@@ -91,10 +91,13 @@ export const useUserStore = defineStore({
       try {
         const { goHome = true, mode, ...loginParams } = params;
         const data = await loginApi(loginParams, mode);
-        const { token } = data;
+        console.log('loginApi',data)
+        const { token,roleId,id,userName } = data;
 
         // save token
         this.setToken(token);
+        this.setUserInfo(data);
+        // return Promise.resolve({token,roleId,id,userName});
         return this.afterLoginAction(goHome);
       } catch (error) {
         return Promise.reject(error);
@@ -103,8 +106,8 @@ export const useUserStore = defineStore({
     async afterLoginAction(goHome?: boolean): Promise<GetUserInfoModel | null> {
       if (!this.getToken) return null;
       // get user info
-      const userInfo = await this.getUserInfoAction();
-
+      const userInfo = this.userInfo
+      // const userInfo = await this.getUserInfoAction();
       const sessionTimeout = this.sessionTimeout;
       if (sessionTimeout) {
         this.setSessionTimeout(false);
@@ -122,7 +125,7 @@ export const useUserStore = defineStore({
       }
       return userInfo;
     },
-    async getUserInfoAction(): Promise<UserInfo | null> {
+    async getUserInfoAction(): Promise<UserInfoV4 | null> {
       if (!this.getToken) return null;
       const userInfo = await getUserInfo();
       const { roles = [] } = userInfo;

+ 99 - 0
src/utils/encodeUtil.ts

@@ -0,0 +1,99 @@
+/*
+ ** 登录密码加密
+  http://face3d.4dage.com:7005/4dzfb-2.0/shop/src/master/platform-admin/src/main/webapp/login.html
+ */
+function randomWord(randomFlag, min, max?) {
+  let str = '';
+  let range = min;
+  const arr = [
+    '0',
+    '1',
+    '2',
+    '3',
+    '4',
+    '5',
+    '6',
+    '7',
+    '8',
+    '9',
+    'a',
+    'b',
+    'c',
+    'd',
+    'e',
+    'f',
+    'g',
+    'h',
+    'i',
+    'j',
+    'k',
+    'l',
+    'm',
+    'n',
+    'o',
+    'p',
+    'q',
+    'r',
+    's',
+    't',
+    'u',
+    'v',
+    'w',
+    'x',
+    'y',
+    'z',
+    'A',
+    'B',
+    'C',
+    'D',
+    'E',
+    'F',
+    'G',
+    'H',
+    'I',
+    'J',
+    'K',
+    'L',
+    'M',
+    'N',
+    'O',
+    'P',
+    'Q',
+    'R',
+    'S',
+    'T',
+    'U',
+    'V',
+    'W',
+    'X',
+    'Y',
+    'Z',
+  ];
+  // 随机产生
+  if (randomFlag) {
+    range = Math.round(Math.random() * (max - min)) + min;
+  }
+  for (let i = 0; i < range; i++) {
+    const pos = Math.round(Math.random() * (arr.length - 1));
+    str += arr[pos];
+  }
+  return str;
+}
+
+export function encodeStr(str, strv = ''): string {
+  const NUM = 2;
+  const front = randomWord(false, 8);
+  const middle = randomWord(false, 8);
+  const end = randomWord(false, 8);
+
+  const str1 = str.substring(0, NUM);
+  const str2 = str.substring(NUM);
+
+  if (strv) {
+    const strv1 = strv.substring(0, NUM);
+    const strv2 = strv.substring(NUM);
+    return [front + str2 + middle + str1 + end, front + strv2 + middle + strv1 + end];
+  }
+
+  return front + str2 + middle + str1 + end;
+}

+ 5 - 8
src/utils/http/axios/Axios.ts

@@ -61,7 +61,7 @@ export class VAxios {
   }
 
   /**
-   * @description: Interceptor configuration 拦截器配置
+   * @description: Interceptor configuration
    */
   private setupInterceptors() {
     const transform = this.getTransform();
@@ -111,10 +111,7 @@ export class VAxios {
     // Response result interceptor error capture
     responseInterceptorsCatch &&
       isFunction(responseInterceptorsCatch) &&
-      this.axiosInstance.interceptors.response.use(undefined, (error) => {
-        // @ts-ignore
-        return responseInterceptorsCatch(this.axiosInstance, error);
-      });
+      this.axiosInstance.interceptors.response.use(undefined, responseInterceptorsCatch);
   }
 
   /**
@@ -199,7 +196,7 @@ export class VAxios {
 
     const opt: RequestOptions = Object.assign({}, requestOptions, options);
 
-    const { beforeRequestHook, requestCatchHook, transformResponseHook } = transform || {};
+    const { beforeRequestHook, requestCatchHook, transformRequestHook } = transform || {};
     if (beforeRequestHook && isFunction(beforeRequestHook)) {
       conf = beforeRequestHook(conf, opt);
     }
@@ -211,9 +208,9 @@ export class VAxios {
       this.axiosInstance
         .request<any, AxiosResponse<Result>>(conf)
         .then((res: AxiosResponse<Result>) => {
-          if (transformResponseHook && isFunction(transformResponseHook)) {
+          if (transformRequestHook && isFunction(transformRequestHook)) {
             try {
-              const ret = transformResponseHook(res, opt);
+              const ret = transformRequestHook(res, opt);
               resolve(ret);
             } catch (err) {
               reject(err || new Error('request error!'));

+ 50 - 49
src/utils/http/axios/axiosTransform.ts

@@ -1,52 +1,53 @@
 /**
  * Data processing class, can be configured according to the project
  */
-import type { AxiosRequestConfig, AxiosResponse } from 'axios';
-import type { RequestOptions, Result } from '/#/axios';
-
-export interface CreateAxiosOptions extends AxiosRequestConfig {
-  authenticationScheme?: string;
-  transform?: AxiosTransform;
-  requestOptions?: RequestOptions;
-}
-
-export abstract class AxiosTransform {
-  /**
-   * @description: Process configuration before request
-   * @description: Process configuration before request
-   */
-  beforeRequestHook?: (config: AxiosRequestConfig, options: RequestOptions) => AxiosRequestConfig;
-
-  /**
-   * @description: 处理响应数据
-   */
-  transformResponseHook?: (res: AxiosResponse<Result>, options: RequestOptions) => any;
-
-  /**
-   * @description: 请求失败处理
-   */
-  requestCatchHook?: (e: Error, options: RequestOptions) => Promise<any>;
-
-  /**
-   * @description: 请求之前的拦截器
-   */
-  requestInterceptors?: (
-    config: AxiosRequestConfig,
-    options: CreateAxiosOptions,
-  ) => AxiosRequestConfig;
-
-  /**
-   * @description: 请求之后的拦截器
-   */
-  responseInterceptors?: (res: AxiosResponse<any>) => AxiosResponse<any>;
-
-  /**
-   * @description: 请求之前的拦截器错误处理
-   */
-  requestInterceptorsCatch?: (error: Error) => void;
-
-  /**
-   * @description: 请求之后的拦截器错误处理
-   */
-  responseInterceptorsCatch?: (axiosInstance: AxiosResponse, error: Error) => void;
-}
+ import type { AxiosRequestConfig, AxiosResponse } from 'axios';
+ import type { RequestOptions, Result } from '/#/axios';
+ 
+ export interface CreateAxiosOptions extends AxiosRequestConfig {
+   authenticationScheme?: string;
+   transform?: AxiosTransform;
+   requestOptions?: RequestOptions;
+ }
+ 
+ export abstract class AxiosTransform {
+   /**
+    * @description: Process configuration before request
+    * @description: Process configuration before request
+    */
+   beforeRequestHook?: (config: AxiosRequestConfig, options: RequestOptions) => AxiosRequestConfig;
+ 
+   /**
+    * @description: Request successfully processed
+    */
+   transformRequestHook?: (res: AxiosResponse<Result>, options: RequestOptions) => any;
+ 
+   /**
+    * @description: 请求失败处理
+    */
+   requestCatchHook?: (e: Error, options: RequestOptions) => Promise<any>;
+ 
+   /**
+    * @description: 请求之前的拦截器
+    */
+   requestInterceptors?: (
+     config: AxiosRequestConfig,
+     options: CreateAxiosOptions,
+   ) => AxiosRequestConfig;
+ 
+   /**
+    * @description: 请求之后的拦截器
+    */
+   responseInterceptors?: (res: AxiosResponse<any>) => AxiosResponse<any>;
+ 
+   /**
+    * @description: 请求之前的拦截器错误处理
+    */
+   requestInterceptorsCatch?: (error: Error) => void;
+ 
+   /**
+    * @description: 请求之后的拦截器错误处理
+    */
+   responseInterceptorsCatch?: (error: Error) => void;
+ }
+ 

+ 36 - 26
src/utils/http/axios/index.ts

@@ -17,7 +17,6 @@ import { useErrorLogStoreWithOut } from '/@/store/modules/errorLog';
 import { useI18n } from '/@/hooks/web/useI18n';
 import { joinTimestamp, formatRequestDate } from './helper';
 import { useUserStoreWithOut } from '/@/store/modules/user';
-import { AxiosRetry } from '/@/utils/http/axios/axiosRetry';
 
 const globSetting = useGlobSetting();
 const urlPrefix = globSetting.urlPrefix;
@@ -28,9 +27,9 @@ const { createMessage, createErrorModal } = useMessage();
  */
 const transform: AxiosTransform = {
   /**
-   * @description: 处理响应数据。如果数据不是预期格式,可直接抛出错误
+   * @description: 处理请求数据。如果数据不是预期格式,可直接抛出错误
    */
-  transformResponseHook: (res: AxiosResponse<Result>, options: RequestOptions) => {
+  transformRequestHook: (res: AxiosResponse<Result>, options: RequestOptions) => {
     const { t } = useI18n();
     const { isTransformResponse, isReturnNativeResponse } = options;
     // 是否返回原生响应头 比如:需要获取响应头时使用该属性
@@ -50,12 +49,24 @@ const transform: AxiosTransform = {
       throw new Error(t('sys.api.apiRequestFailed'));
     }
     //  这里 code,result,message为 后台统一的字段,需要在 types.ts内修改为项目自己的接口返回格式
-    const { code, result, message } = data;
 
+    const { code, error, message } = data;
+    // TODO
     // 这里逻辑可以根据项目进行修改
-    const hasSuccess = data && Reflect.has(data, 'code') && code === ResultEnum.SUCCESS;
+    const hasSuccess =
+      data &&
+      Reflect.has(data, 'code') &&
+      (code === ResultEnum.SUCCESS || code === ResultEnum.NORMAL);
+      console.log('list',hasSuccess,'data',data)
     if (hasSuccess) {
-      return result;
+      const converterResult = data.message;
+      const converterMessage = error;
+      Reflect.set(data, 'result', converterResult);
+      Reflect.set(data, 'message', converterMessage);
+      // data.result = converterResult;
+      // data.message = converterMessage;
+      delete data.error;
+      return data.data || data;
     }
 
     // 在此处根据自己项目的实际情况对不同的code执行不同的操作
@@ -68,10 +79,20 @@ const transform: AxiosTransform = {
         userStore.setToken(undefined);
         userStore.logout(true);
         break;
+      case ResultEnum.JAVA_ERROR:
+        if (error) {
+          timeoutMsg = error;
+        }
+        break;
       default:
-        if (message) {
-          timeoutMsg = message;
+        // if (message) {
+        //   timeoutMsg = message;
+        // }
+        //TODO 由于后端HACKCODE error当信息string
+        if (error) {
+          timeoutMsg = error;
         }
+        break;
     }
 
     // errorMessageMode=‘modal’的时候会显示modal错误弹窗,而不是消息提示,用于一些比较重要的错误
@@ -81,7 +102,7 @@ const transform: AxiosTransform = {
     } else if (options.errorMessageMode === 'message') {
       createMessage.error(timeoutMsg);
     }
-
+    console.log('timeoutMsg', timeoutMsg);
     throw new Error(timeoutMsg || t('sys.api.apiRequestFailed'));
   },
 
@@ -111,7 +132,7 @@ const transform: AxiosTransform = {
     } else {
       if (!isString(params)) {
         formatDate && formatRequestDate(params);
-        if (Reflect.has(config, 'data') && config.data && (Object.keys(config.data).length > 0 || config.data instanceof FormData)) {
+        if (Reflect.has(config, 'data') && config.data && Object.keys(config.data).length > 0) {
           config.data = data;
           config.params = params;
         } else {
@@ -142,7 +163,10 @@ const transform: AxiosTransform = {
     const token = getToken();
     if (token && (config as Recordable)?.requestOptions?.withToken !== false) {
       // jwt token
-      (config as Recordable).headers.Authorization = options.authenticationScheme
+      // (config as Recordable).headers.Authorization = options.authenticationScheme
+      //   ? `${options.authenticationScheme} ${token}`
+      //   : token;
+      (config as Recordable).headers.token = options.authenticationScheme
         ? `${options.authenticationScheme} ${token}`
         : token;
     }
@@ -159,7 +183,7 @@ const transform: AxiosTransform = {
   /**
    * @description: 响应错误处理
    */
-  responseInterceptorsCatch: (axiosInstance: AxiosResponse, error: any) => {
+  responseInterceptorsCatch: (error: any) => {
     const { t } = useI18n();
     const errorLogStore = useErrorLogStoreWithOut();
     errorLogStore.addAjaxErrorInfo(error);
@@ -190,21 +214,12 @@ const transform: AxiosTransform = {
     }
 
     checkStatus(error?.response?.status, msg, errorMessageMode);
-
-    // 添加自动重试机制 保险起见 只针对GET请求
-    const retryRequest = new AxiosRetry();
-    const { isOpenRetry } = config.requestOptions.retryRequest;
-    config.method?.toUpperCase() === RequestEnum.GET &&
-      isOpenRetry &&
-      // @ts-ignore
-      retryRequest.retry(axiosInstance, error);
     return Promise.reject(error);
   },
 };
 
 function createAxios(opt?: Partial<CreateAxiosOptions>) {
   return new VAxios(
-    // 深度合并
     deepMerge(
       {
         // See https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication#authentication_schemes
@@ -244,11 +259,6 @@ function createAxios(opt?: Partial<CreateAxiosOptions>) {
           ignoreCancelToken: true,
           // 是否携带token
           withToken: true,
-          retryRequest: {
-            isOpenRetry: true,
-            count: 5,
-            waitTime: 100,
-          },
         },
       },
       opt || {},

+ 1 - 1
src/views/demo/table/ExpandTable.vue

@@ -5,7 +5,7 @@
   >
     <BasicTable @register="registerTable">
       <template #expandedRowRender="{ record }">
-        <span>No: {{ record.no }} </span>
+        <span>No: {{ record.no }} expandedRowRender</span>
       </template>
       <template #bodyCell="{ column, record }">
         <template v-if="column.key === 'action'">

+ 65 - 0
src/views/operate/data.tsx

@@ -0,0 +1,65 @@
+import { BasicColumn } from '/@/components/Table/src/types/table';
+
+import { Badge } from 'ant-design-vue';
+
+export const refundTimeTableSchema: BasicColumn[] = [
+  {
+    title: '时间',
+    width: 150,
+    dataIndex: 't1',
+  },
+  {
+    title: '当前进度',
+    width: 150,
+    dataIndex: 't2',
+  },
+  {
+    title: '状态',
+    width: 150,
+    dataIndex: 't3',
+    customRender: ({ record }) => {
+      return <Badge status="success" text={record.t3} />;
+    },
+  },
+  {
+    title: '操作员ID	',
+    width: 150,
+    dataIndex: 't4',
+  },
+  {
+    title: '耗时',
+    width: 150,
+    dataIndex: 't5',
+  },
+];
+
+export const refundTimeTableData: any[] = [
+  {
+    t1: '2017-10-01 14:10',
+    t2: '联系客户',
+    t3: '进行中',
+    t4: '取货员 ID1234',
+    t5: '5mins',
+  },
+  {
+    t1: '2017-10-01 14:10',
+    t2: '取货员出发',
+    t3: '成功',
+    t4: '取货员 ID1234',
+    t5: '5mins',
+  },
+  {
+    t1: '2017-10-01 14:10',
+    t2: '取货员接单',
+    t3: '成功',
+    t4: '系统',
+    t5: '5mins',
+  },
+  {
+    t1: '2017-10-01 14:10',
+    t2: '申请审批通过',
+    t3: '成功',
+    t4: '用户',
+    t5: '1h',
+  },
+];

+ 57 - 0
src/views/operate/messageList.vue

@@ -0,0 +1,57 @@
+<template>
+  <PageWrapper title="单号:234231029431" contentBackground>
+    <template #extra>
+      <a-button> 操作一 </a-button>
+      <a-button> 操作二 </a-button>
+      <a-button type="primary"> 主操作 </a-button>
+    </template>
+
+    <template #footer>
+      <a-tabs default-active-key="1">
+        <a-tab-pane key="1" tab="详情" />
+        <a-tab-pane key="2" tab="规则" />
+      </a-tabs>
+    </template>
+
+    <div class="pt-4 m-4 desc-wrap">
+      <BasicTable @register="registerTimeTable" />
+    </div>
+  </PageWrapper>
+</template>
+<script lang="ts">
+  import { defineComponent } from 'vue';
+  import { BasicTable, useTable } from '/@/components/Table';
+  import { PageWrapper } from '/@/components/Page';
+  import { Divider, Card, Empty, Descriptions, Steps, Tabs } from 'ant-design-vue';
+
+  import { refundTimeTableSchema, refundTimeTableData } from './data';
+  export default defineComponent({
+    components: {
+      BasicTable,
+      PageWrapper,
+      [Divider.name]: Divider,
+      [Card.name]: Card,
+      Empty,
+      [Descriptions.name]: Descriptions,
+      [Descriptions.Item.name]: Descriptions.Item,
+      [Steps.name]: Steps,
+      [Steps.Step.name]: Steps.Step,
+      [Tabs.name]: Tabs,
+      [Tabs.TabPane.name]: Tabs.TabPane,
+    },
+    setup() {
+      const [registerTimeTable] = useTable({
+        title: '退货进度',
+        columns: refundTimeTableSchema,
+        pagination: false,
+        dataSource: refundTimeTableData,
+        showIndexColumn: false,
+        scroll: { y: 300 },
+      });
+
+      return {
+        registerTimeTable,
+      };
+    },
+  });
+</script>

+ 222 - 0
src/views/operate/newsList.vue

@@ -0,0 +1,222 @@
+<template>
+    <BasicTable @register="registerTable">
+      <template #toolbar>
+        <a-button type="primary" @click="exportExcel"> 导出</a-button>
+      </template>
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'action'">
+          <TableAction
+            stopButtonPropagation
+            :actions="[
+              {
+                label: '删除',
+                icon: 'ic:outline-delete-outline',
+                onClick: handleDelete.bind(null, record),
+              },
+            ]"
+            :dropDownActions="[
+              {
+                label: '启用',
+                popConfirm: {
+                  title: '是否启用?',
+                  confirm: handleOpen.bind(null, record),
+                },
+              },
+            ]"
+          />
+        </template>
+      </template>
+    </BasicTable>
+</template>
+<script lang="ts">
+  import { defineComponent, h } from 'vue';
+  import { BasicTable, useTable, TableAction, BasicColumn, TableImg, FormProps } from '/@/components/Table';
+  import { PageWrapper } from '/@/components/Page';
+  import { Time } from '/@/components/Time';
+  import { ListApi } from '/@/api/operate'
+  import { demoListApi } from '/@/api/demo/table';
+  import { Descriptions } from 'ant-design-vue';
+  import { useI18n } from '/@/hooks/web/useI18n';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import { Switch } from 'ant-design-vue';
+
+  export default defineComponent({
+    components: { 
+      BasicTable, 
+      TableAction, 
+      PageWrapper,
+      TableImg,
+      [Descriptions.name]: Descriptions,
+      [Descriptions.Item.name]: Descriptions.Item,
+    },
+    setup() {
+      const { t } = useI18n();
+      const { createMessage } = useMessage();
+      const columns: BasicColumn[] = [
+        
+        {
+          title: '新闻标题',
+          dataIndex: 'newsTitle',
+          ellipsis: false,
+          width: 250,
+        },
+        {
+          title: '来源',
+          dataIndex: 'orderSn',
+          ellipsis: false,
+          width: 120,
+        },
+        {
+          title: '类型',
+          dataIndex: 'nickName',
+          width: 80,
+        },
+        {
+          title: '创建人',
+          dataIndex: 'brandName',
+          width: 80,
+        },
+        {
+          title: '创建时间',
+          dataIndex: 'createTime',
+          width: 150,
+          customRender: ({ record }) => {
+            return (
+              record.createTime &&
+              h(Time, {
+                value: record.createTime,
+                mode: 'datetime',
+              })
+            );
+          },
+        },
+        {
+          title: '发布时间',
+          dataIndex: 'createTime',
+          width: 150,
+          customRender: ({ record }) => {
+            return (
+              record.createTime &&
+              h(Time, {
+                value: record.createTime,
+                mode: 'datetime',
+              })
+            );
+          },
+        },
+        {
+          title: '是否显示',
+          dataIndex: 'isOnSale',
+          width: 80,
+          customRender: ({ record }) => {
+            if (!Reflect.has(record, 'pendingStatus')) {
+              record.pendingStatus = false;
+            }
+            return h(Switch, {
+              checked: record.isOnSale === 1,
+              checkedChildren: '上架',
+              unCheckedChildren: '下架',
+              loading: false,
+              onChange: async (checked: boolean) => {
+                record.pendingStatus = true;
+                const id: string = record.id || '';
+                const newStatus = checked ? 1 : 0;
+                if (checked) {
+                  Reflect.set(record, 'isOnSale', newStatus);
+                  await EnSaleApi([id]);
+                } else {
+                  Reflect.set(record, 'isOnSale', newStatus);
+                  await UnSaleApi([id]);
+                }
+                createMessage.success(t('common.optSuccess'));
+              },
+            });
+          },
+        },
+        {
+          title: '置顶',
+          dataIndex: 'isOnSale',
+          width: 80,
+          customRender: ({ record }) => {
+            if (!Reflect.has(record, 'pendingStatus')) {
+              record.pendingStatus = false;
+            }
+            return h(Switch, {
+              checked: record.isOnSale === 1,
+              checkedChildren: '是',
+              unCheckedChildren: '否',
+              loading: false,
+              onChange: async (checked: boolean) => {
+                record.pendingStatus = true;
+                // const id: string = record.id || '';
+                const newStatus = checked ? 1 : 0;
+                if (checked) {
+                  Reflect.set(record, 'isOnSale', newStatus);
+                  // await EnSaleApi([id]);
+                } else {
+                  Reflect.set(record, 'isOnSale', newStatus);
+                  // await UnSaleApi([id]);
+                }
+                createMessage.success(t('common.optSuccess'));
+              },
+            });
+          },
+        },
+      ];
+      const searchForm: Partial<FormProps> = {
+        labelWidth: 100,
+        schemas: [
+          {
+            field: 'sceneName',
+            label: t('routes.operate.releaseTime'),
+            component: 'RangePicker',
+            componentProps: {
+              maxLength: 100,
+              format: 'YYYY-MM-DD HH:mm',
+              showTime: true,
+            },
+            colProps: {
+              xl: 8,
+              xxl: 8,
+            },
+          },
+          {
+            field: 'type',
+            label: t('routes.operate.newsTitle'),
+            component: 'Input',
+            colProps: {
+              xl: 7,
+              xxl: 7,
+            },
+          },
+        ],
+      };
+      const [registerTable] = useTable({
+        api: ListApi,
+        title: '新闻列表',
+        columns: columns,
+        useSearchForm: true,
+        formConfig: searchForm,
+        showTableSetting: true,
+        showIndexColumn:false,
+        rowKey: 'id',
+        canResize: false,
+      });
+      function handleDelete(record: Recordable) {
+        console.log('点击了删除', record);
+      }
+      function handleOpen(record: Recordable) {
+        console.log('点击了启用', record);
+      }
+      function exportExcel(record: Recordable) {
+        console.log('点击了导出', record);
+      }
+      return {
+        registerTable,
+        handleDelete,
+        handleOpen,
+        exportExcel,
+      };
+    },
+  });
+</script>

+ 273 - 0
src/views/operate/recruitList.vue

@@ -0,0 +1,273 @@
+<template>
+  <PageWrapper
+    title="可展开表格"
+    content="TableAction组件可配置stopButtonPropagation来阻止操作按钮的点击事件冒泡,以便配合Table组件的expandRowByClick"
+  >
+    <BasicTable @register="registerTable">
+      <template #toolbar>
+        <a-button type="primary" @click="exportExcel"> 导出</a-button>
+      </template>
+      <!-- <template #expandedRowRender="{ record }">
+        <div>
+          <a-descriptions title="信息组" :column="3">
+            <a-descriptions-item label="负责人"> 林东东{{record.no}} </a-descriptions-item>
+            <a-descriptions-item label="角色码"> 1234567 </a-descriptions-item>
+            <a-descriptions-item label="所属部门"> XX公司 - YY部 </a-descriptions-item>
+            <a-descriptions-item label="过期时间"> 2017-08-08 </a-descriptions-item>
+            <a-descriptions-item label="描述" :span="2">
+              这段描述很长很长很长很长很长很长很长很长很长很长很长很长很长很长...
+            </a-descriptions-item>
+            <a-descriptions-item label="商品图片" :span="1">
+              <TableImg style="margin: 0"
+                :size="120"
+                :simpleShow="true"
+                :imgList="[record.appListPicUrl || 'http://zfb-4dkankan.oss-cn-shenzhen.aliyuncs.com/sceneLogo/1653644220202_3ee8b3c006e74012a82f2b286b2f4914.png']"
+              />
+            </a-descriptions-item>
+          </a-descriptions>
+        </div>
+      </template> -->
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'action'">
+          <TableAction
+            stopButtonPropagation
+            :actions="[
+              {
+                label: '删除',
+                icon: 'ic:outline-delete-outline',
+                onClick: handleDelete.bind(null, record),
+              },
+            ]"
+            :dropDownActions="[
+              {
+                label: '启用',
+                popConfirm: {
+                  title: '是否启用?',
+                  confirm: handleOpen.bind(null, record),
+                },
+              },
+            ]"
+          />
+        </template>
+      </template>
+    </BasicTable>
+  </PageWrapper>
+</template>
+<script lang="ts">
+  import { defineComponent, h } from 'vue';
+  import { BasicTable, useTable, TableAction, BasicColumn, TableImg, FormProps } from '/@/components/Table';
+  import { PageWrapper } from '/@/components/Page';
+  import { Time } from '/@/components/Time';
+  import { demoListApi } from '/@/api/demo/table';
+  import { Descriptions } from 'ant-design-vue';
+  import { useI18n } from '/@/hooks/web/useI18n';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import { Switch } from 'ant-design-vue';
+
+  export default defineComponent({
+    components: { 
+      BasicTable, 
+      TableAction, 
+      PageWrapper,
+      TableImg,
+      [Descriptions.name]: Descriptions,
+      [Descriptions.Item.name]: Descriptions.Item,
+    },
+    setup() {
+      const { t } = useI18n();
+      const { createMessage } = useMessage();
+      const columns: BasicColumn[] = [
+        {
+          title: '时间',
+          dataIndex: 'createTime',
+          width: 150,
+          customRender: ({ record }) => {
+            return (
+              record.createTime &&
+              h(Time, {
+                value: record.createTime,
+                mode: 'datetime',
+              })
+            );
+          },
+        },
+        {
+          title: '订单号',
+          dataIndex: 'orderSn',
+          ellipsis: false,
+          width: 180,
+        },
+        {
+          title: '用户名',
+          dataIndex: 'nickName',
+          width: 80,
+        },
+        {
+          title: '订单金额',
+          dataIndex: 'brandName',
+          width: 80,
+        },
+        {
+          title: '支付方式',
+          dataIndex: 'orderType',
+          slots: { customRender: 'orderType' },
+          width: 80,
+        },
+        {
+          title: '订单状态',
+          dataIndex: 'orderStatus',
+          slots: { customRender: 'orderStatus' },
+          width: 80,
+        },
+        {
+          title: '上架状态',
+          dataIndex: 'isOnSale',
+          width: 180,
+          customRender: ({ record }) => {
+            if (!Reflect.has(record, 'pendingStatus')) {
+              record.pendingStatus = false;
+            }
+            return h(Switch, {
+              checked: record.isOnSale === 1,
+              checkedChildren: '上架',
+              unCheckedChildren: '下架',
+              loading: false,
+              onChange: async (checked: boolean) => {
+                record.pendingStatus = true;
+                const id: string = record.id || '';
+                const newStatus = checked ? 1 : 0;
+                if (checked) {
+                  Reflect.set(record, 'isOnSale', newStatus);
+                  await EnSaleApi([id]);
+                } else {
+                  Reflect.set(record, 'isOnSale', newStatus);
+                  await UnSaleApi([id]);
+                }
+                createMessage.success(t('common.optSuccess'));
+              },
+            });
+          },
+        },
+        {
+          title: '置顶',
+          dataIndex: 'isOnSale',
+          width: 180,
+          customRender: ({ record }) => {
+            if (!Reflect.has(record, 'pendingStatus')) {
+              record.pendingStatus = false;
+            }
+            return h(Switch, {
+              checked: record.isOnSale === 1,
+              checkedChildren: '是',
+              unCheckedChildren: '否',
+              loading: false,
+              onChange: async (checked: boolean) => {
+                record.pendingStatus = true;
+                // const id: string = record.id || '';
+                const newStatus = checked ? 1 : 0;
+                if (checked) {
+                  Reflect.set(record, 'isOnSale', newStatus);
+                  // await EnSaleApi([id]);
+                } else {
+                  Reflect.set(record, 'isOnSale', newStatus);
+                  // await UnSaleApi([id]);
+                }
+                createMessage.success(t('common.optSuccess'));
+              },
+            });
+          },
+        },
+      ];
+      const searchForm: Partial<FormProps> = {
+        labelWidth: 100,
+        schemas: [
+          {
+            field: 'sceneName',
+            label: t('routes.scenes.anchorRoom'),
+            component: 'Input',
+            componentProps: {
+              maxLength: 100,
+            },
+            colProps: {
+              xl: 5,
+              xxl: 5,
+            },
+          },
+          {
+            field: 'type',
+            label: t('common.type'),
+            component: 'ApiSelect',
+            colProps: {
+              xl: 5,
+              xxl: 5,
+            },
+            componentProps: {
+              // api: brandTypeListApi,
+              resultField: 'list',
+              labelField: 'name',
+              valueField: 'brandType',
+              params: {
+                page: 1,
+                limit: 1000,
+              },
+            },
+          },
+          {
+            field: 'livestreamStatus',
+            label: t('routes.scenes.livestreamStatus'),
+            component: 'Select',
+            colProps: {
+              xl: 5,
+              xxl: 5,
+            },
+            componentProps: {
+              options: [
+                {
+                  label: t('common.all'),
+                  value: '',
+                  key: '0',
+                },
+                {
+                  label: t('common.yes'),
+                  value: 1,
+                  key: '1',
+                },
+                {
+                  label: t('common.no'),
+                  value: 0,
+                  key: '2',
+                },
+              ],
+            },
+          },
+        ],
+      };
+      const [registerTable] = useTable({
+        api: demoListApi,
+        title: '可展开表格演示',
+        titleHelpMessage: ['已启用expandRowByClick', '已启用stopButtonPropagation'],
+        columns: columns,
+        useSearchForm: true,
+        formConfig: searchForm,
+        showTableSetting: true,
+        rowKey: 'id',
+        canResize: false,
+      });
+      function handleDelete(record: Recordable) {
+        console.log('点击了删除', record);
+      }
+      function handleOpen(record: Recordable) {
+        console.log('点击了启用', record);
+      }
+      function exportExcel(record: Recordable) {
+        console.log('点击了导出', record);
+      }
+      return {
+        registerTable,
+        handleDelete,
+        handleOpen,
+        exportExcel,
+      };
+    },
+  });
+</script>

+ 215 - 0
src/views/order/cameraDetail.vue

@@ -0,0 +1,215 @@
+<template>
+  <div>
+    <PageWrapper :title="`订单号:${ids}`" contentBackground>
+      <Description
+        size="middle"
+        title="商品详情"
+        :bordered="false"
+        :useCollapse="true"
+        :column="3"
+        :data="refundData"
+        :schema="goodsSchema"
+      />
+      <div>
+        <a-row :gutter="16">
+          <a-col :span="24">
+            <a-card
+              v-for="item in refundData.goodsList"
+              :key="item.id"
+              :hoverable="true"
+              class="`list-card__card`"
+            >
+              <div class="`list-card__card-title`">
+                <TableImg :size="50" :simpleShow="true" :imgList="[item.listPicUrl]" />
+              </div>
+              <div class="`list-card__card-detail`">
+                <div>{{ item.goodsName }}</div>
+                ¥{{ item.retailPrice }} X {{ item.number }}
+              </div>
+            </a-card>
+          </a-col>
+        </a-row>
+      </div>
+      <a-divider />
+      <Description
+        size="middle"
+        title="订单信息"
+        :bordered="false"
+        :column="3"
+        :data="refundData"
+        :schema="refundSchema"
+      />
+      <a-divider />
+      <Description
+        size="middle"
+        title="用户信息"
+        :bordered="false"
+        :column="3"
+        :data="refundData"
+        :schema="personSchema"
+      />
+      <a-card title="物流进度" :bordered="false">
+        <a-steps :current="current" progress-dot size="small">
+          <a-step title="创建订单">
+            <template #description>
+              <Time v-if="refundSchema.addTime" :value="refundSchema.addTime" mode="datetime" />
+            </template>
+          </a-step>
+          <a-step title="已付款">
+            <template #description>
+              <Time v-if="refundSchema.payTime" :value="refundSchema.payTime" mode="datetime" />
+            </template>
+          </a-step>
+          <a-step title="物流配送">
+            <template #description>
+              <p>{{ refundSchema.shippingName }} </p>
+            </template>
+          </a-step>
+          <a-step title="已配送" />
+        </a-steps>
+      </a-card>
+
+      <template #extra>
+        <a-button
+          type="primary"
+          @click="
+            router.push({
+              path: '/order/list',
+            })
+          "
+        >
+          返回
+        </a-button>
+      </template>
+    </PageWrapper>
+  </div>
+</template>
+<script lang="ts">
+  import { defineComponent, onMounted, ref } from 'vue';
+  import { PageWrapper } from '/@/components/Page';
+  import { Description } from '/@/components/Description/index';
+  import { useRoute, useRouter } from 'vue-router';
+  import { useTabs } from '/@/hooks/web/useTabs';
+  import { TableImg } from '/@/components/Table';
+  import { refundSchema, personData, goodsSchema, personSchema } from './data';
+  import { Divider, Card, Row, Col, Steps } from 'ant-design-vue';
+  import { GetOrderInfoApi } from '/@/api/order/list';
+  import { Time } from '/@/components/Time';
+
+  export default defineComponent({
+    components: {
+      PageWrapper,
+      Description,
+      TableImg,
+      Time,
+      [Divider.name]: Divider,
+      [Card.name]: Card,
+      [Steps.name]: Steps,
+      [Steps.Step.name]: Steps.Step,
+      [Row.name]: Row,
+      [Col.name]: Col,
+    },
+    setup() {
+      const refundData = ref({});
+      const current = ref(0);
+      const route = useRoute();
+      const { setTitle } = useTabs();
+      const router = useRouter();
+      const ids = route.params.id;
+      const id = Number(route.query.id);
+      const brandId = Number(route.query.brandId);
+      setTitle(`No.${route.params.id} - 订单详情`);
+      onMounted(async () => {
+        let res = await GetOrderInfoApi({
+          id,
+          brandId,
+        });
+        refundData.value = res;
+        let ordercurrent = 0;
+        switch (res.orderStatus) {
+          case 0:
+            ordercurrent = 0;
+            break;
+          case 101:
+            ordercurrent = 0;
+            break;
+          case 201:
+            ordercurrent = 1;
+            break;
+          case 1:
+            ordercurrent = 2;
+            break;
+          case 2:
+            ordercurrent = 3;
+            break;
+          case 501:
+            ordercurrent = 3;
+            break;
+          default:
+            ordercurrent = 1;
+            break;
+        }
+        current.value = ordercurrent;
+      });
+      return {
+        refundSchema,
+        refundData,
+        personData,
+        personSchema,
+        goodsSchema,
+        router,
+        ids,
+        current,
+      };
+    },
+  });
+</script>
+<style lang="less" scoped>
+  :deep(.ant-card-body) {
+    padding: 16px;
+    display: flex;
+    justify-content: space-between;
+    width: 100%;
+  }
+  .list-card {
+    &__link {
+      margin-top: 10px;
+      font-size: 14px;
+
+      a {
+        margin-right: 30px;
+      }
+
+      span {
+        margin-left: 5px;
+      }
+    }
+
+    &__card {
+      margin-bottom: -8px;
+      display: flex;
+
+      &-title {
+        flex: 1;
+        margin-bottom: 5px;
+        font-size: 16px;
+        font-weight: 500;
+        color: @text-color;
+
+        .icon {
+          margin-top: -5px;
+          margin-right: 10px;
+          font-size: 38px !important;
+        }
+      }
+
+      &-detail {
+        flex: 1;
+        padding-top: 10px;
+        padding-left: 30px;
+        font-size: 14px;
+        color: @text-color-secondary;
+      }
+    }
+  }
+</style>

+ 180 - 0
src/views/order/cameraList.vue

@@ -0,0 +1,180 @@
+<template>
+  <PageWrapper
+    title="可展开表格"
+    content="TableAction组件可配置stopButtonPropagation来阻止操作按钮的点击事件冒泡,以便配合Table组件的expandRowByClick"
+  >
+    <BasicTable @register="registerTable">
+      <template #expandedRowRender="{ record }">
+        <div>
+          <a-descriptions title="信息组" :column="3">
+            <a-descriptions-item label="负责人"> 林东东{{record.no}} </a-descriptions-item>
+            <a-descriptions-item label="角色码"> 1234567 </a-descriptions-item>
+            <a-descriptions-item label="所属部门"> XX公司 - YY部 </a-descriptions-item>
+            <a-descriptions-item label="过期时间"> 2017-08-08 </a-descriptions-item>
+            <a-descriptions-item label="描述" :span="2">
+              这段描述很长很长很长很长很长很长很长很长很长很长很长很长很长很长...
+            </a-descriptions-item>
+            <a-descriptions-item label="商品图片" :span="1">
+              <TableImg style="margin: 0"
+                :size="120"
+                :simpleShow="true"
+                :imgList="[record.appListPicUrl || 'http://zfb-4dkankan.oss-cn-shenzhen.aliyuncs.com/sceneLogo/1653644220202_3ee8b3c006e74012a82f2b286b2f4914.png']"
+              />
+            </a-descriptions-item>
+          </a-descriptions>
+        </div>
+      </template>
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'action'">
+          <TableAction
+            stopButtonPropagation
+            :actions="[
+              {
+                label: '删除',
+                icon: 'ic:outline-delete-outline',
+                onClick: handleDelete.bind(null, record),
+              },
+            ]"
+            :dropDownActions="[
+              {
+                label: '启用',
+                popConfirm: {
+                  title: '是否启用?',
+                  confirm: handleOpen.bind(null, record),
+                },
+              },
+            ]"
+          />
+        </template>
+      </template>
+    </BasicTable>
+  </PageWrapper>
+</template>
+<script lang="ts">
+  import { defineComponent, h } from 'vue';
+  import { BasicTable, useTable, TableAction, BasicColumn, TableImg } from '/@/components/Table';
+  import { PageWrapper } from '/@/components/Page';
+  import { Time } from '/@/components/Time';
+  import { demoListApi } from '/@/api/demo/table';
+  import { Descriptions } from 'ant-design-vue';
+
+  export default defineComponent({
+    components: { 
+      BasicTable, 
+      TableAction, 
+      PageWrapper,
+      TableImg,
+      [Descriptions.name]: Descriptions,
+      [Descriptions.Item.name]: Descriptions.Item,
+    },
+    setup() {
+      const columns: BasicColumn[] = [
+        {
+          title: '时间',
+          dataIndex: 'createTime',
+          width: 150,
+          customRender: ({ record }) => {
+            return (
+              record.createTime &&
+              h(Time, {
+                value: record.createTime,
+                mode: 'datetime',
+              })
+            );
+          },
+        },
+        {
+          title: '订单号',
+          dataIndex: 'orderSn',
+          ellipsis: false,
+          width: 180,
+        },
+        {
+          title: '用户名',
+          dataIndex: 'nickName',
+          width: 80,
+        },
+        {
+          title: '订单金额',
+          dataIndex: 'brandName',
+          width: 80,
+        },
+        {
+          title: '支付方式',
+          dataIndex: 'orderType',
+          slots: { customRender: 'orderType' },
+          width: 80,
+        },
+        {
+          title: '订单状态',
+          dataIndex: 'orderStatus',
+          slots: { customRender: 'orderStatus' },
+          width: 80,
+        },
+        // {
+        //   title: '发货状态',
+        //   dataIndex: 'shippingStatus',
+        //   sorter: true,
+        //   slots: { customRender: 'shippingStatus' },
+        //   width: 80,
+        // },
+        // {
+        //   title: '付款状态',
+        //   dataIndex: 'payStatus',
+        //   slots: { customRender: 'payStatus' },
+        //   width: 60,
+        // },
+        // {
+        //   title: '快递公司',
+        //   dataIndex: 'shipingName',
+        //   width: 60,
+        // },
+        // {
+        //   title: '快递单号',
+        //   dataIndex: 'shippingNo',
+        //   width: 80,
+        // },
+        // {
+        //   title: '实际支付金额',
+        //   dataIndex: 'actualPrice',
+        //   width: 80,
+        // },
+        // {
+        //   title: '下单时间',
+        //   dataIndex: 'addTime',
+        //   width: 120,
+        //   slots: { customRender: 'addTime' },
+        // },
+        // {
+        //   title: '操作',
+        //   dataIndex: '',
+        //   slots: { customRender: 'action' },
+        //   fixed: 'right',
+        //   align: 'center',
+        //   width: 250,
+        // },
+      ];
+      const [registerTable] = useTable({
+        api: demoListApi,
+        title: '可展开表格演示',
+        titleHelpMessage: ['已启用expandRowByClick', '已启用stopButtonPropagation'],
+        columns: columns,
+        rowKey: 'id',
+        canResize: false,
+        expandRowByClick: true,
+      });
+      function handleDelete(record: Recordable) {
+        console.log('点击了删除', record);
+      }
+      function handleOpen(record: Recordable) {
+        console.log('点击了启用', record);
+      }
+
+      return {
+        registerTable,
+        handleDelete,
+        handleOpen,
+      };
+    },
+  });
+</script>

+ 93 - 0
src/views/order/confirmModal.vue

@@ -0,0 +1,93 @@
+<template>
+  <BasicModal v-bind="$attrs" @register="register" title="发货" @ok="handleSubmit">
+    <BasicForm @register="registerForm" />
+  </BasicModal>
+</template>
+<script lang="ts">
+  import { defineComponent, reactive } from 'vue';
+  import { BasicForm, useForm, FormSchema } from '/@/components/Form/index';
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import { ShippingListApi, sendGoods, GetOrderInfoApi } from '/@/api/order/list'; //GetOrderInfoApi
+
+  export default defineComponent({
+    name: 'ConfirmModal',
+    components: { BasicModal, BasicForm },
+    emits: ['success', 'register', 'reload'],
+    setup(_, { emit }) {
+      // const isUpdate = ref(true);
+      let modalRecord = reactive({
+        id: 0,
+        brandId: 0,
+      });
+      const formSchema: FormSchema[] = [
+        {
+          field: 'shippingId',
+          label: '快递公司',
+          component: 'ApiSelect',
+          componentProps: {
+            api: ShippingListApi,
+            resultField: 'list',
+            labelField: 'name',
+            valueField: 'id',
+            immediate: true,
+            onChange: function (_, opt) {
+              Reflect.set(modalRecord, 'shippingName', opt.label);
+            },
+            params: {
+              page: 1,
+              limit: 1000,
+            },
+            required: true,
+          },
+        },
+        {
+          field: 'orderSn',
+          label: '快递单号',
+          component: 'Input',
+          required: true,
+          componentProps: {
+            maxLength: 100,
+          },
+        },
+      ];
+      const [registerForm, { validate, resetFields }] = useForm({
+        labelWidth: 120,
+        schemas: formSchema,
+        showActionButtonGroup: false,
+        baseColProps: { lg: 20, md: 20, offset: 1 },
+      });
+
+      const [register, { closeModal }] = useModalInner((data) => {
+        data && onDataReceive(data);
+      });
+      function onDataReceive(data) {
+        console.log('data', data);
+        modalRecord = { ...data };
+        // modalRecord.brandId = data.brandId;
+      }
+      async function handleSubmit() {
+        console.log('handleSubmit');
+        try {
+          const values = await validate();
+          const orderInfo = await GetOrderInfoApi({
+            id: modalRecord.id,
+            brandId: modalRecord.brandId,
+          });
+          Reflect.set(modalRecord, 'shippingNo', values.orderSn);
+          Reflect.set(modalRecord, 'shippingId', values.shippingId);
+          await sendGoods({ ...orderInfo, ...modalRecord });
+          resetFields();
+          closeModal();
+          emit('reload');
+        } catch (error) {}
+      }
+
+      return {
+        register,
+        registerForm,
+        handleSubmit,
+        closeModal,
+      };
+    },
+  });
+</script>

+ 294 - 0
src/views/order/data.tsx

@@ -0,0 +1,294 @@
+import { DescItem } from '/@/components/Description/index';
+import { BasicColumn } from '/@/components/Table/src/types/table';
+import { Button } from '/@/components/Button';
+// import { h } from 'vue';
+// import { Time } from '/@/components/Time';
+import { Badge } from 'ant-design-vue';
+
+function renderOrderStatusLabel(type: number): string {
+  switch (type) {
+    case 0:
+      return '待付款';
+    case 101:
+      return '已取消';
+    case 201:
+      return '已付款';
+    case 1:
+      return '已发货';
+    case 2:
+      return '已收货';
+    case 501:
+      return '完成';
+    default:
+      return '';
+  }
+}
+
+function formatDayTime(val) {
+  if (val) {
+    const date = new Date(val);
+    const Y = date.getFullYear();
+    let M = date.getMonth() + 1;
+    let D = date.getDate();
+
+    if (M < 10) {
+      M = '0' + M;
+    }
+    if (D < 10) {
+      D = '0' + D;
+    }
+
+    return Y + '-' + M + '-' + D;
+  } else {
+    return '';
+  }
+}
+export const personData = {
+  b1: '付小小',
+  b2: '18100000000',
+  b3: '菜鸟仓储',
+  b4: '浙江省杭州市西湖区万塘路18号',
+  b5: '无',
+};
+export const goodsSchema: DescItem[] = [
+  // {
+  //   field: 'goodsList',
+  //   label: '商品信息',
+  // },
+  {
+    field: 'payStatus',
+    label: '付款状态',
+    render: (curVal, _) => {
+      return `${renderOrderStatusLabel(curVal) || ''}`;
+    },
+  },
+  {
+    field: 'nickName',
+    label: '会员昵称',
+  },
+  {
+    field: 'payTime',
+    label: '付款时间',
+    render: (curVal, _) => {
+      return `${formatDayTime(curVal)}`;
+    },
+  },
+  {
+    field: 'brandName',
+    label: '产品型号',
+  },
+  {
+    field: 'addTime',
+    label: '下单时间',
+    render: (curVal, _) => {
+      return `${formatDayTime(curVal)}`;
+    },
+  },
+  {
+    field: 'goodsPrice',
+    label: '商品总价',
+  },
+];
+
+export const refundSchema: DescItem[] = [
+  {
+    field: 'orderSn',
+    label: '订单号',
+  },
+  {
+    field: 'orderStatus',
+    label: '订单状态',
+  },
+  {
+    field: 'a3',
+    label: '发货状态',
+  },
+  {
+    field: 'payId',
+    label: '支付单号',
+  },
+  {
+    field: 'payTime',
+    label: '支付时间',
+    render: (curVal, _) => {
+      return `${formatDayTime(curVal)}`;
+    },
+  },
+  {
+    field: 'consignee',
+    label: '收货人',
+  },
+  {
+    field: 'address',
+    label: '收货地址',
+  },
+  {
+    field: 'mobile',
+    label: '联系电话',
+  },
+  {
+    field: 'shippingName',
+    label: '快递公司',
+  },
+  {
+    field: 'shippingNo',
+    label: '快递单号',
+  },
+  {
+    field: 'shippingFee',
+    label: '快递费用',
+  },
+];
+export const personSchema: DescItem[] = [
+  {
+    field: 'userName',
+    label: '用户姓名',
+  },
+  {
+    field: 'mobile',
+    label: '联系电话',
+  },
+  {
+    field: 'city',
+    label: '取货地址',
+    render: (curVal, _) => {
+      return `${curVal} ${_.address}`;
+    },
+  },
+];
+
+export const refundTableSchema: BasicColumn[] = [
+  {
+    title: '商品编号',
+    width: 150,
+    dataIndex: 't1',
+    customRender: ({ record }) => {
+      return (
+        <Button type="link" size="small">
+          {() => record.t1}
+        </Button>
+      );
+    },
+  },
+  {
+    title: '商品名称',
+    width: 150,
+    dataIndex: 't2',
+  },
+  {
+    title: '商品条码',
+    width: 150,
+    dataIndex: 't3',
+  },
+  {
+    title: '单价	',
+    width: 150,
+    dataIndex: 't4',
+  },
+  {
+    title: '数量(件)	',
+    width: 150,
+    dataIndex: 't5',
+  },
+  {
+    title: '金额',
+    width: 150,
+    dataIndex: 't6',
+  },
+];
+export const refundTimeTableSchema: BasicColumn[] = [
+  {
+    title: '时间',
+    width: 150,
+    dataIndex: 't1',
+  },
+  {
+    title: '当前进度',
+    width: 150,
+    dataIndex: 't2',
+  },
+  {
+    title: '状态',
+    width: 150,
+    dataIndex: 't3',
+    customRender: ({ record }) => {
+      return <Badge status="success" text={record.t3} />;
+    },
+  },
+  {
+    title: '操作员ID	',
+    width: 150,
+    dataIndex: 't4',
+  },
+  {
+    title: '耗时',
+    width: 150,
+    dataIndex: 't5',
+  },
+];
+
+export const refundTableData: any[] = [
+  {
+    t1: 1234561,
+    t2: '矿泉水 550ml',
+    t3: '12421432143214321',
+    t4: '2.00',
+    t5: 1,
+    t6: 2.0,
+  },
+  {
+    t1: 1234562,
+    t2: '矿泉水 550ml',
+    t3: '12421432143214321',
+    t4: '2.00',
+    t5: 2,
+    t6: 2.0,
+  },
+  {
+    t1: 1234562,
+    t2: '矿泉水 550ml',
+    t3: '12421432143214321',
+    t4: '2.00',
+    t5: 2,
+    t6: 2.0,
+  },
+  {
+    t1: 1234562,
+    t2: '矿泉水 550ml',
+    t3: '12421432143214321',
+    t4: '2.00',
+    t5: 2,
+    t6: 2.0,
+  },
+];
+
+export const refundTimeTableData: any[] = [
+  {
+    t1: '2017-10-01 14:10',
+    t2: '联系客户',
+    t3: '进行中',
+    t4: '取货员 ID1234',
+    t5: '5mins',
+  },
+  {
+    t1: '2017-10-01 14:10',
+    t2: '取货员出发',
+    t3: '成功',
+    t4: '取货员 ID1234',
+    t5: '5mins',
+  },
+  {
+    t1: '2017-10-01 14:10',
+    t2: '取货员接单',
+    t3: '成功',
+    t4: '系统',
+    t5: '5mins',
+  },
+  {
+    t1: '2017-10-01 14:10',
+    t2: '申请审批通过',
+    t3: '成功',
+    t4: '用户',
+    t5: '1h',
+  },
+];

+ 215 - 0
src/views/order/detail.vue

@@ -0,0 +1,215 @@
+<template>
+  <div>
+    <PageWrapper :title="`订单号:${ids}`" contentBackground>
+      <Description
+        size="middle"
+        title="商品详情"
+        :bordered="false"
+        :useCollapse="true"
+        :column="3"
+        :data="refundData"
+        :schema="goodsSchema"
+      />
+      <div>
+        <a-row :gutter="16">
+          <a-col :span="24">
+            <a-card
+              v-for="item in refundData.goodsList"
+              :key="item.id"
+              :hoverable="true"
+              class="`list-card__card`"
+            >
+              <div class="`list-card__card-title`">
+                <TableImg :size="50" :simpleShow="true" :imgList="[item.listPicUrl]" />
+              </div>
+              <div class="`list-card__card-detail`">
+                <div>{{ item.goodsName }}</div>
+                ¥{{ item.retailPrice }} X {{ item.number }}
+              </div>
+            </a-card>
+          </a-col>
+        </a-row>
+      </div>
+      <a-divider />
+      <Description
+        size="middle"
+        title="订单信息"
+        :bordered="false"
+        :column="3"
+        :data="refundData"
+        :schema="refundSchema"
+      />
+      <a-divider />
+      <Description
+        size="middle"
+        title="用户信息"
+        :bordered="false"
+        :column="3"
+        :data="refundData"
+        :schema="personSchema"
+      />
+      <a-card title="物流进度" :bordered="false">
+        <a-steps :current="current" progress-dot size="small">
+          <a-step title="创建订单">
+            <template #description>
+              <Time v-if="refundSchema.addTime" :value="refundSchema.addTime" mode="datetime" />
+            </template>
+          </a-step>
+          <a-step title="已付款">
+            <template #description>
+              <Time v-if="refundSchema.payTime" :value="refundSchema.payTime" mode="datetime" />
+            </template>
+          </a-step>
+          <a-step title="物流配送">
+            <template #description>
+              <p>{{ refundSchema.shippingName }} </p>
+            </template>
+          </a-step>
+          <a-step title="已配送" />
+        </a-steps>
+      </a-card>
+
+      <template #extra>
+        <a-button
+          type="primary"
+          @click="
+            router.push({
+              path: '/order/list',
+            })
+          "
+        >
+          返回
+        </a-button>
+      </template>
+    </PageWrapper>
+  </div>
+</template>
+<script lang="ts">
+  import { defineComponent, onMounted, ref } from 'vue';
+  import { PageWrapper } from '/@/components/Page';
+  import { Description } from '/@/components/Description/index';
+  import { useRoute, useRouter } from 'vue-router';
+  import { useTabs } from '/@/hooks/web/useTabs';
+  import { TableImg } from '/@/components/Table';
+  import { refundSchema, personData, goodsSchema, personSchema } from './data';
+  import { Divider, Card, Row, Col, Steps } from 'ant-design-vue';
+  import { GetOrderInfoApi } from '/@/api/order/list';
+  import { Time } from '/@/components/Time';
+
+  export default defineComponent({
+    components: {
+      PageWrapper,
+      Description,
+      TableImg,
+      Time,
+      [Divider.name]: Divider,
+      [Card.name]: Card,
+      [Steps.name]: Steps,
+      [Steps.Step.name]: Steps.Step,
+      [Row.name]: Row,
+      [Col.name]: Col,
+    },
+    setup() {
+      const refundData = ref({});
+      const current = ref(0);
+      const route = useRoute();
+      const { setTitle } = useTabs();
+      const router = useRouter();
+      const ids = route.params.id;
+      const id = Number(route.query.id);
+      const brandId = Number(route.query.brandId);
+      setTitle(`No.${route.params.id} - 订单详情`);
+      onMounted(async () => {
+        let res = await GetOrderInfoApi({
+          id,
+          brandId,
+        });
+        refundData.value = res;
+        let ordercurrent = 0;
+        switch (res.orderStatus) {
+          case 0:
+            ordercurrent = 0;
+            break;
+          case 101:
+            ordercurrent = 0;
+            break;
+          case 201:
+            ordercurrent = 1;
+            break;
+          case 1:
+            ordercurrent = 2;
+            break;
+          case 2:
+            ordercurrent = 3;
+            break;
+          case 501:
+            ordercurrent = 3;
+            break;
+          default:
+            ordercurrent = 1;
+            break;
+        }
+        current.value = ordercurrent;
+      });
+      return {
+        refundSchema,
+        refundData,
+        personData,
+        personSchema,
+        goodsSchema,
+        router,
+        ids,
+        current,
+      };
+    },
+  });
+</script>
+<style lang="less" scoped>
+  :deep(.ant-card-body) {
+    padding: 16px;
+    display: flex;
+    justify-content: space-between;
+    width: 100%;
+  }
+  .list-card {
+    &__link {
+      margin-top: 10px;
+      font-size: 14px;
+
+      a {
+        margin-right: 30px;
+      }
+
+      span {
+        margin-left: 5px;
+      }
+    }
+
+    &__card {
+      margin-bottom: -8px;
+      display: flex;
+
+      &-title {
+        flex: 1;
+        margin-bottom: 5px;
+        font-size: 16px;
+        font-weight: 500;
+        color: @text-color;
+
+        .icon {
+          margin-top: -5px;
+          margin-right: 10px;
+          font-size: 38px !important;
+        }
+      }
+
+      &-detail {
+        flex: 1;
+        padding-top: 10px;
+        padding-left: 30px;
+        font-size: 14px;
+        color: @text-color-secondary;
+      }
+    }
+  }
+</style>

+ 215 - 0
src/views/order/downloadDetail.vue

@@ -0,0 +1,215 @@
+<template>
+  <div>
+    <PageWrapper :title="`订单号:${ids}`" contentBackground>
+      <Description
+        size="middle"
+        title="商品详情"
+        :bordered="false"
+        :useCollapse="true"
+        :column="3"
+        :data="refundData"
+        :schema="goodsSchema"
+      />
+      <div>
+        <a-row :gutter="16">
+          <a-col :span="24">
+            <a-card
+              v-for="item in refundData.goodsList"
+              :key="item.id"
+              :hoverable="true"
+              class="`list-card__card`"
+            >
+              <div class="`list-card__card-title`">
+                <TableImg :size="50" :simpleShow="true" :imgList="[item.listPicUrl]" />
+              </div>
+              <div class="`list-card__card-detail`">
+                <div>{{ item.goodsName }}</div>
+                ¥{{ item.retailPrice }} X {{ item.number }}
+              </div>
+            </a-card>
+          </a-col>
+        </a-row>
+      </div>
+      <a-divider />
+      <Description
+        size="middle"
+        title="订单信息"
+        :bordered="false"
+        :column="3"
+        :data="refundData"
+        :schema="refundSchema"
+      />
+      <a-divider />
+      <Description
+        size="middle"
+        title="用户信息"
+        :bordered="false"
+        :column="3"
+        :data="refundData"
+        :schema="personSchema"
+      />
+      <a-card title="物流进度" :bordered="false">
+        <a-steps :current="current" progress-dot size="small">
+          <a-step title="创建订单">
+            <template #description>
+              <Time v-if="refundSchema.addTime" :value="refundSchema.addTime" mode="datetime" />
+            </template>
+          </a-step>
+          <a-step title="已付款">
+            <template #description>
+              <Time v-if="refundSchema.payTime" :value="refundSchema.payTime" mode="datetime" />
+            </template>
+          </a-step>
+          <a-step title="物流配送">
+            <template #description>
+              <p>{{ refundSchema.shippingName }} </p>
+            </template>
+          </a-step>
+          <a-step title="已配送" />
+        </a-steps>
+      </a-card>
+
+      <template #extra>
+        <a-button
+          type="primary"
+          @click="
+            router.push({
+              path: '/order/list',
+            })
+          "
+        >
+          返回
+        </a-button>
+      </template>
+    </PageWrapper>
+  </div>
+</template>
+<script lang="ts">
+  import { defineComponent, onMounted, ref } from 'vue';
+  import { PageWrapper } from '/@/components/Page';
+  import { Description } from '/@/components/Description/index';
+  import { useRoute, useRouter } from 'vue-router';
+  import { useTabs } from '/@/hooks/web/useTabs';
+  import { TableImg } from '/@/components/Table';
+  import { refundSchema, personData, goodsSchema, personSchema } from './data';
+  import { Divider, Card, Row, Col, Steps } from 'ant-design-vue';
+  import { GetOrderInfoApi } from '/@/api/order/list';
+  import { Time } from '/@/components/Time';
+
+  export default defineComponent({
+    components: {
+      PageWrapper,
+      Description,
+      TableImg,
+      Time,
+      [Divider.name]: Divider,
+      [Card.name]: Card,
+      [Steps.name]: Steps,
+      [Steps.Step.name]: Steps.Step,
+      [Row.name]: Row,
+      [Col.name]: Col,
+    },
+    setup() {
+      const refundData = ref({});
+      const current = ref(0);
+      const route = useRoute();
+      const { setTitle } = useTabs();
+      const router = useRouter();
+      const ids = route.params.id;
+      const id = Number(route.query.id);
+      const brandId = Number(route.query.brandId);
+      setTitle(`No.${route.params.id} - 订单详情`);
+      onMounted(async () => {
+        let res = await GetOrderInfoApi({
+          id,
+          brandId,
+        });
+        refundData.value = res;
+        let ordercurrent = 0;
+        switch (res.orderStatus) {
+          case 0:
+            ordercurrent = 0;
+            break;
+          case 101:
+            ordercurrent = 0;
+            break;
+          case 201:
+            ordercurrent = 1;
+            break;
+          case 1:
+            ordercurrent = 2;
+            break;
+          case 2:
+            ordercurrent = 3;
+            break;
+          case 501:
+            ordercurrent = 3;
+            break;
+          default:
+            ordercurrent = 1;
+            break;
+        }
+        current.value = ordercurrent;
+      });
+      return {
+        refundSchema,
+        refundData,
+        personData,
+        personSchema,
+        goodsSchema,
+        router,
+        ids,
+        current,
+      };
+    },
+  });
+</script>
+<style lang="less" scoped>
+  :deep(.ant-card-body) {
+    padding: 16px;
+    display: flex;
+    justify-content: space-between;
+    width: 100%;
+  }
+  .list-card {
+    &__link {
+      margin-top: 10px;
+      font-size: 14px;
+
+      a {
+        margin-right: 30px;
+      }
+
+      span {
+        margin-left: 5px;
+      }
+    }
+
+    &__card {
+      margin-bottom: -8px;
+      display: flex;
+
+      &-title {
+        flex: 1;
+        margin-bottom: 5px;
+        font-size: 16px;
+        font-weight: 500;
+        color: @text-color;
+
+        .icon {
+          margin-top: -5px;
+          margin-right: 10px;
+          font-size: 38px !important;
+        }
+      }
+
+      &-detail {
+        flex: 1;
+        padding-top: 10px;
+        padding-left: 30px;
+        font-size: 14px;
+        color: @text-color-secondary;
+      }
+    }
+  }
+</style>

+ 212 - 0
src/views/order/downloadList.vue

@@ -0,0 +1,212 @@
+<template>
+  <PageWrapper
+    title="可展开表格"
+    content="TableAction组件可配置stopButtonPropagation来阻止操作按钮的点击事件冒泡,以便配合Table组件的expandRowByClick"
+  >
+    <BasicTable @register="registerTable">
+      <template #toolbar>
+        <a-button type="primary" @click="exportExcel"> 导出</a-button>
+      </template>
+      <!-- <template #expandedRowRender="{ record }">
+        <div>
+          <a-descriptions title="信息组" :column="3">
+            <a-descriptions-item label="负责人"> 林东东{{record.no}} </a-descriptions-item>
+            <a-descriptions-item label="角色码"> 1234567 </a-descriptions-item>
+            <a-descriptions-item label="所属部门"> XX公司 - YY部 </a-descriptions-item>
+            <a-descriptions-item label="过期时间"> 2017-08-08 </a-descriptions-item>
+            <a-descriptions-item label="描述" :span="2">
+              这段描述很长很长很长很长很长很长很长很长很长很长很长很长很长很长...
+            </a-descriptions-item>
+            <a-descriptions-item label="商品图片" :span="1">
+              <TableImg style="margin: 0"
+                :size="120"
+                :simpleShow="true"
+                :imgList="[record.appListPicUrl || 'http://zfb-4dkankan.oss-cn-shenzhen.aliyuncs.com/sceneLogo/1653644220202_3ee8b3c006e74012a82f2b286b2f4914.png']"
+              />
+            </a-descriptions-item>
+          </a-descriptions>
+        </div>
+      </template> -->
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'action'">
+          <TableAction
+            stopButtonPropagation
+            :actions="[
+              {
+                label: '删除',
+                icon: 'ic:outline-delete-outline',
+                onClick: handleDelete.bind(null, record),
+              },
+            ]"
+            :dropDownActions="[
+              {
+                label: '启用',
+                popConfirm: {
+                  title: '是否启用?',
+                  confirm: handleOpen.bind(null, record),
+                },
+              },
+            ]"
+          />
+        </template>
+      </template>
+    </BasicTable>
+  </PageWrapper>
+</template>
+<script lang="ts">
+  import { defineComponent, h } from 'vue';
+  import { BasicTable, useTable, TableAction, BasicColumn, TableImg, FormProps } from '/@/components/Table';
+  import { PageWrapper } from '/@/components/Page';
+  import { Time } from '/@/components/Time';
+  import { demoListApi } from '/@/api/demo/table';
+  import { Descriptions } from 'ant-design-vue';
+  import { useI18n } from '/@/hooks/web/useI18n';
+
+  export default defineComponent({
+    components: { 
+      BasicTable, 
+      TableAction, 
+      PageWrapper,
+      TableImg,
+      [Descriptions.name]: Descriptions,
+      [Descriptions.Item.name]: Descriptions.Item,
+    },
+    setup() {
+      const { t } = useI18n();
+      const columns: BasicColumn[] = [
+        {
+          title: '时间',
+          dataIndex: 'createTime',
+          width: 150,
+          customRender: ({ record }) => {
+            return (
+              record.createTime &&
+              h(Time, {
+                value: record.createTime,
+                mode: 'datetime',
+              })
+            );
+          },
+        },
+        {
+          title: '订单号',
+          dataIndex: 'orderSn',
+          ellipsis: false,
+          width: 180,
+        },
+        {
+          title: '用户名',
+          dataIndex: 'nickName',
+          width: 80,
+        },
+        {
+          title: '订单金额',
+          dataIndex: 'brandName',
+          width: 80,
+        },
+        {
+          title: '支付方式',
+          dataIndex: 'orderType',
+          slots: { customRender: 'orderType' },
+          width: 80,
+        },
+        {
+          title: '订单状态',
+          dataIndex: 'orderStatus',
+          slots: { customRender: 'orderStatus' },
+          width: 80,
+        },
+      ];
+      const searchForm: Partial<FormProps> = {
+        labelWidth: 100,
+        schemas: [
+          {
+            field: 'sceneName',
+            label: t('routes.scenes.anchorRoom'),
+            component: 'Input',
+            componentProps: {
+              maxLength: 100,
+            },
+            colProps: {
+              xl: 5,
+              xxl: 5,
+            },
+          },
+          {
+            field: 'type',
+            label: t('common.type'),
+            component: 'ApiSelect',
+            colProps: {
+              xl: 5,
+              xxl: 5,
+            },
+            componentProps: {
+              // api: brandTypeListApi,
+              resultField: 'list',
+              labelField: 'name',
+              valueField: 'brandType',
+              params: {
+                page: 1,
+                limit: 1000,
+              },
+            },
+          },
+          {
+            field: 'livestreamStatus',
+            label: t('routes.scenes.livestreamStatus'),
+            component: 'Select',
+            colProps: {
+              xl: 5,
+              xxl: 5,
+            },
+            componentProps: {
+              options: [
+                {
+                  label: t('common.all'),
+                  value: '',
+                  key: '0',
+                },
+                {
+                  label: t('common.yes'),
+                  value: 1,
+                  key: '1',
+                },
+                {
+                  label: t('common.no'),
+                  value: 0,
+                  key: '2',
+                },
+              ],
+            },
+          },
+        ],
+      };
+      const [registerTable] = useTable({
+        api: demoListApi,
+        title: '可展开表格演示',
+        titleHelpMessage: ['已启用expandRowByClick', '已启用stopButtonPropagation'],
+        columns: columns,
+        useSearchForm: true,
+        formConfig: searchForm,
+        showTableSetting: true,
+        rowKey: 'id',
+        canResize: false,
+      });
+      function handleDelete(record: Recordable) {
+        console.log('点击了删除', record);
+      }
+      function handleOpen(record: Recordable) {
+        console.log('点击了启用', record);
+      }
+      function exportExcel(record: Recordable) {
+        console.log('点击了导出', record);
+      }
+      return {
+        registerTable,
+        handleDelete,
+        handleOpen,
+        exportExcel,
+      };
+    },
+  });
+</script>

+ 215 - 0
src/views/order/equityDetail.vue

@@ -0,0 +1,215 @@
+<template>
+  <div>
+    <PageWrapper :title="`订单号:${ids}`" contentBackground>
+      <Description
+        size="middle"
+        title="商品详情"
+        :bordered="false"
+        :useCollapse="true"
+        :column="3"
+        :data="refundData"
+        :schema="goodsSchema"
+      />
+      <div>
+        <a-row :gutter="16">
+          <a-col :span="24">
+            <a-card
+              v-for="item in refundData.goodsList"
+              :key="item.id"
+              :hoverable="true"
+              class="`list-card__card`"
+            >
+              <div class="`list-card__card-title`">
+                <TableImg :size="50" :simpleShow="true" :imgList="[item.listPicUrl]" />
+              </div>
+              <div class="`list-card__card-detail`">
+                <div>{{ item.goodsName }}</div>
+                ¥{{ item.retailPrice }} X {{ item.number }}
+              </div>
+            </a-card>
+          </a-col>
+        </a-row>
+      </div>
+      <a-divider />
+      <Description
+        size="middle"
+        title="订单信息"
+        :bordered="false"
+        :column="3"
+        :data="refundData"
+        :schema="refundSchema"
+      />
+      <a-divider />
+      <Description
+        size="middle"
+        title="用户信息"
+        :bordered="false"
+        :column="3"
+        :data="refundData"
+        :schema="personSchema"
+      />
+      <a-card title="物流进度" :bordered="false">
+        <a-steps :current="current" progress-dot size="small">
+          <a-step title="创建订单">
+            <template #description>
+              <Time v-if="refundSchema.addTime" :value="refundSchema.addTime" mode="datetime" />
+            </template>
+          </a-step>
+          <a-step title="已付款">
+            <template #description>
+              <Time v-if="refundSchema.payTime" :value="refundSchema.payTime" mode="datetime" />
+            </template>
+          </a-step>
+          <a-step title="物流配送">
+            <template #description>
+              <p>{{ refundSchema.shippingName }} </p>
+            </template>
+          </a-step>
+          <a-step title="已配送" />
+        </a-steps>
+      </a-card>
+
+      <template #extra>
+        <a-button
+          type="primary"
+          @click="
+            router.push({
+              path: '/order/list',
+            })
+          "
+        >
+          返回
+        </a-button>
+      </template>
+    </PageWrapper>
+  </div>
+</template>
+<script lang="ts">
+  import { defineComponent, onMounted, ref } from 'vue';
+  import { PageWrapper } from '/@/components/Page';
+  import { Description } from '/@/components/Description/index';
+  import { useRoute, useRouter } from 'vue-router';
+  import { useTabs } from '/@/hooks/web/useTabs';
+  import { TableImg } from '/@/components/Table';
+  import { refundSchema, personData, goodsSchema, personSchema } from './data';
+  import { Divider, Card, Row, Col, Steps } from 'ant-design-vue';
+  import { GetOrderInfoApi } from '/@/api/order/list';
+  import { Time } from '/@/components/Time';
+
+  export default defineComponent({
+    components: {
+      PageWrapper,
+      Description,
+      TableImg,
+      Time,
+      [Divider.name]: Divider,
+      [Card.name]: Card,
+      [Steps.name]: Steps,
+      [Steps.Step.name]: Steps.Step,
+      [Row.name]: Row,
+      [Col.name]: Col,
+    },
+    setup() {
+      const refundData = ref({});
+      const current = ref(0);
+      const route = useRoute();
+      const { setTitle } = useTabs();
+      const router = useRouter();
+      const ids = route.params.id;
+      const id = Number(route.query.id);
+      const brandId = Number(route.query.brandId);
+      setTitle(`No.${route.params.id} - 订单详情`);
+      onMounted(async () => {
+        let res = await GetOrderInfoApi({
+          id,
+          brandId,
+        });
+        refundData.value = res;
+        let ordercurrent = 0;
+        switch (res.orderStatus) {
+          case 0:
+            ordercurrent = 0;
+            break;
+          case 101:
+            ordercurrent = 0;
+            break;
+          case 201:
+            ordercurrent = 1;
+            break;
+          case 1:
+            ordercurrent = 2;
+            break;
+          case 2:
+            ordercurrent = 3;
+            break;
+          case 501:
+            ordercurrent = 3;
+            break;
+          default:
+            ordercurrent = 1;
+            break;
+        }
+        current.value = ordercurrent;
+      });
+      return {
+        refundSchema,
+        refundData,
+        personData,
+        personSchema,
+        goodsSchema,
+        router,
+        ids,
+        current,
+      };
+    },
+  });
+</script>
+<style lang="less" scoped>
+  :deep(.ant-card-body) {
+    padding: 16px;
+    display: flex;
+    justify-content: space-between;
+    width: 100%;
+  }
+  .list-card {
+    &__link {
+      margin-top: 10px;
+      font-size: 14px;
+
+      a {
+        margin-right: 30px;
+      }
+
+      span {
+        margin-left: 5px;
+      }
+    }
+
+    &__card {
+      margin-bottom: -8px;
+      display: flex;
+
+      &-title {
+        flex: 1;
+        margin-bottom: 5px;
+        font-size: 16px;
+        font-weight: 500;
+        color: @text-color;
+
+        .icon {
+          margin-top: -5px;
+          margin-right: 10px;
+          font-size: 38px !important;
+        }
+      }
+
+      &-detail {
+        flex: 1;
+        padding-top: 10px;
+        padding-left: 30px;
+        font-size: 14px;
+        color: @text-color-secondary;
+      }
+    }
+  }
+</style>

+ 212 - 0
src/views/order/equityList.vue

@@ -0,0 +1,212 @@
+<template>
+  <PageWrapper
+    title="可展开表格"
+    content="TableAction组件可配置stopButtonPropagation来阻止操作按钮的点击事件冒泡,以便配合Table组件的expandRowByClick"
+  >
+    <BasicTable @register="registerTable">
+      <template #toolbar>
+        <a-button type="primary" @click="exportExcel"> 导出</a-button>
+      </template>
+      <!-- <template #expandedRowRender="{ record }">
+        <div>
+          <a-descriptions title="信息组" :column="3">
+            <a-descriptions-item label="负责人"> 林东东{{record.no}} </a-descriptions-item>
+            <a-descriptions-item label="角色码"> 1234567 </a-descriptions-item>
+            <a-descriptions-item label="所属部门"> XX公司 - YY部 </a-descriptions-item>
+            <a-descriptions-item label="过期时间"> 2017-08-08 </a-descriptions-item>
+            <a-descriptions-item label="描述" :span="2">
+              这段描述很长很长很长很长很长很长很长很长很长很长很长很长很长很长...
+            </a-descriptions-item>
+            <a-descriptions-item label="商品图片" :span="1">
+              <TableImg style="margin: 0"
+                :size="120"
+                :simpleShow="true"
+                :imgList="[record.appListPicUrl || 'http://zfb-4dkankan.oss-cn-shenzhen.aliyuncs.com/sceneLogo/1653644220202_3ee8b3c006e74012a82f2b286b2f4914.png']"
+              />
+            </a-descriptions-item>
+          </a-descriptions>
+        </div>
+      </template> -->
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'action'">
+          <TableAction
+            stopButtonPropagation
+            :actions="[
+              {
+                label: '删除',
+                icon: 'ic:outline-delete-outline',
+                onClick: handleDelete.bind(null, record),
+              },
+            ]"
+            :dropDownActions="[
+              {
+                label: '启用',
+                popConfirm: {
+                  title: '是否启用?',
+                  confirm: handleOpen.bind(null, record),
+                },
+              },
+            ]"
+          />
+        </template>
+      </template>
+    </BasicTable>
+  </PageWrapper>
+</template>
+<script lang="ts">
+  import { defineComponent, h } from 'vue';
+  import { BasicTable, useTable, TableAction, BasicColumn, TableImg, FormProps } from '/@/components/Table';
+  import { PageWrapper } from '/@/components/Page';
+  import { Time } from '/@/components/Time';
+  import { demoListApi } from '/@/api/demo/table';
+  import { Descriptions } from 'ant-design-vue';
+  import { useI18n } from '/@/hooks/web/useI18n';
+
+  export default defineComponent({
+    components: { 
+      BasicTable, 
+      TableAction, 
+      PageWrapper,
+      TableImg,
+      [Descriptions.name]: Descriptions,
+      [Descriptions.Item.name]: Descriptions.Item,
+    },
+    setup() {
+      const { t } = useI18n();
+      const columns: BasicColumn[] = [
+        {
+          title: '时间',
+          dataIndex: 'createTime',
+          width: 150,
+          customRender: ({ record }) => {
+            return (
+              record.createTime &&
+              h(Time, {
+                value: record.createTime,
+                mode: 'datetime',
+              })
+            );
+          },
+        },
+        {
+          title: '订单号',
+          dataIndex: 'orderSn',
+          ellipsis: false,
+          width: 180,
+        },
+        {
+          title: '用户名',
+          dataIndex: 'nickName',
+          width: 80,
+        },
+        {
+          title: '订单金额',
+          dataIndex: 'brandName',
+          width: 80,
+        },
+        {
+          title: '支付方式',
+          dataIndex: 'orderType',
+          slots: { customRender: 'orderType' },
+          width: 80,
+        },
+        {
+          title: '订单状态',
+          dataIndex: 'orderStatus',
+          slots: { customRender: 'orderStatus' },
+          width: 80,
+        },
+      ];
+      const searchForm: Partial<FormProps> = {
+        labelWidth: 100,
+        schemas: [
+          {
+            field: 'sceneName',
+            label: t('routes.scenes.anchorRoom'),
+            component: 'Input',
+            componentProps: {
+              maxLength: 100,
+            },
+            colProps: {
+              xl: 5,
+              xxl: 5,
+            },
+          },
+          {
+            field: 'type',
+            label: t('common.type'),
+            component: 'ApiSelect',
+            colProps: {
+              xl: 5,
+              xxl: 5,
+            },
+            componentProps: {
+              // api: brandTypeListApi,
+              resultField: 'list',
+              labelField: 'name',
+              valueField: 'brandType',
+              params: {
+                page: 1,
+                limit: 1000,
+              },
+            },
+          },
+          {
+            field: 'livestreamStatus',
+            label: t('routes.scenes.livestreamStatus'),
+            component: 'Select',
+            colProps: {
+              xl: 5,
+              xxl: 5,
+            },
+            componentProps: {
+              options: [
+                {
+                  label: t('common.all'),
+                  value: '',
+                  key: '0',
+                },
+                {
+                  label: t('common.yes'),
+                  value: 1,
+                  key: '1',
+                },
+                {
+                  label: t('common.no'),
+                  value: 0,
+                  key: '2',
+                },
+              ],
+            },
+          },
+        ],
+      };
+      const [registerTable] = useTable({
+        api: demoListApi,
+        title: '可展开表格演示',
+        titleHelpMessage: ['已启用expandRowByClick', '已启用stopButtonPropagation'],
+        columns: columns,
+        useSearchForm: true,
+        formConfig: searchForm,
+        showTableSetting: true,
+        rowKey: 'id',
+        canResize: false,
+      });
+      function handleDelete(record: Recordable) {
+        console.log('点击了删除', record);
+      }
+      function handleOpen(record: Recordable) {
+        console.log('点击了启用', record);
+      }
+      function exportExcel(record: Recordable) {
+        console.log('点击了导出', record);
+      }
+      return {
+        registerTable,
+        handleDelete,
+        handleOpen,
+        exportExcel,
+      };
+    },
+  });
+</script>

+ 432 - 0
src/views/order/list.vue

@@ -0,0 +1,432 @@
+<template>
+  <div class="p-4">
+    <BasicTable @register="registerTable" :rowSelection="{ type: 'checkbox' }">
+      <template #toolbar>
+        <!-- <a-button type="primary" @click="exportExcel"> 导出</a-button> -->
+      </template>
+      <template #cover="{ record }">
+        <TableImg :size="150" :simpleShow="true" :imgList="[record.cover]" />
+      </template>
+      <template #orderType="{ record }"> {{ renderOrderTypeLabel(record.orderType) }} </template>
+      <template #orderStatus="{ record }">
+        {{ renderOrderStatusLabel(record.orderStatus) }}
+      </template>
+      <template #shippingStatus="{ record }">
+        {{ rendershippingStatusLabel(record.shippingStatus) }}
+      </template>
+      <template #payStatus="{ record }">
+        {{ renderpayStatusLabel(record.payStatus) }}
+      </template>
+      <template #addTime="{ record }">
+        <Time :value="record.addTime" mode="datetime" />
+      </template>
+      <template #action="{ record }">
+        <TableAction
+          :actions="[
+            {
+              label: '发货',
+              color: 'warning',
+              disabled: record.shippingStatus == 1,
+              onClick: sendPackage.bind(null, record),
+            },
+            {
+              label: '详情',
+              onClick: () => {
+                go(
+                  `/order/list/detail/${record.orderSn}?brandId=${record.brandId}&id=${record.id}`,
+                );
+              },
+            },
+            {
+              label: '收货',
+              color: 'warning',
+              disabled: record.orderStatus !== 1,
+              popConfirm: {
+                title: '是否确认收货',
+                confirm: handleConfirmReceive.bind(null, record),
+              },
+            },
+            {
+              label: '打印',
+              color: 'error',
+              onClick: printDetail.bind(null, record),
+            },
+          ]"
+        />
+      </template>
+    </BasicTable>
+    <ConfirmModal @register="registerConfirmModal" @reload="reload" />
+    <PrintModal @register="registerPrintModal" @reload="reload" />
+  </div>
+</template>
+<script lang="ts">
+  import { defineComponent } from 'vue';
+  import {
+    BasicTable,
+    useTable,
+    BasicColumn,
+    FormProps,
+    TableAction,
+    TableImg,
+  } from '/@/components/Table';
+  import { useMessage } from '/@/hooks/web/useMessage';
+
+  import {
+    ListApi,
+    BrandListApi,
+    GetOrderInfoApi,
+    exportExcelApi,
+    confirmOrder,
+  } from '/@/api/order/list';
+  import { useI18n } from '/@/hooks/web/useI18n';
+  // import { useCopyToClipboard } from '/@/hooks/web/useCopyToClipboard';
+  import { useGo } from '/@/hooks/web/usePage';
+  import { Time } from '/@/components/Time';
+
+  import { useModal } from '/@/components/Modal';
+  import ConfirmModal from './confirmModal.vue';
+  import PrintModal from './printModal.vue';
+  import {
+    downloadByUrl,
+    // downloadByData,
+    // downloadByBase64,
+    // downloadByOnlineUrl,
+  } from '/@/utils/file/download';
+
+  export default defineComponent({
+    components: {
+      BasicTable,
+      TableAction,
+      TableImg,
+      Time,
+      ConfirmModal,
+      PrintModal,
+    },
+    setup() {
+      const { createMessage } = useMessage();
+      const go = useGo();
+      const { t } = useI18n();
+      const columns: BasicColumn[] = [
+        {
+          title: 'ID',
+          dataIndex: 'id',
+          fixed: 'left',
+          width: 60,
+        },
+        {
+          title: '订单号',
+          dataIndex: 'orderSn',
+          ellipsis: false,
+          width: 180,
+        },
+        {
+          title: '会员昵称',
+          dataIndex: 'nickName',
+          width: 80,
+        },
+        {
+          title: 'VR场景',
+          dataIndex: 'brandName',
+          width: 80,
+        },
+        {
+          title: '订单类型',
+          dataIndex: 'orderType',
+          slots: { customRender: 'orderType' },
+          sorter: true,
+          width: 80,
+        },
+        {
+          title: '订单状态',
+          dataIndex: 'orderStatus',
+          sorter: true,
+          slots: { customRender: 'orderStatus' },
+          width: 80,
+        },
+        {
+          title: '发货状态',
+          dataIndex: 'shippingStatus',
+          sorter: true,
+          slots: { customRender: 'shippingStatus' },
+          width: 80,
+        },
+        {
+          title: '付款状态',
+          dataIndex: 'payStatus',
+          slots: { customRender: 'payStatus' },
+          width: 60,
+        },
+        {
+          title: '快递公司',
+          dataIndex: 'shipingName',
+          width: 60,
+        },
+        {
+          title: '快递单号',
+          dataIndex: 'shippingNo',
+          width: 80,
+        },
+        {
+          title: '实际支付金额',
+          dataIndex: 'actualPrice',
+          width: 80,
+        },
+        {
+          title: '下单时间',
+          dataIndex: 'addTime',
+          width: 120,
+          slots: { customRender: 'addTime' },
+        },
+        {
+          title: '操作',
+          dataIndex: '',
+          slots: { customRender: 'action' },
+          fixed: 'right',
+          align: 'center',
+          width: 250,
+        },
+      ];
+
+      const searchForm: Partial<FormProps> = {
+        labelWidth: 100,
+        schemas: [
+          {
+            field: 'orderSn',
+            label: '订单号',
+            component: 'Input',
+            componentProps: {
+              maxLength: 100,
+            },
+            colProps: {
+              xl: 5,
+              xxl: 5,
+            },
+          },
+          {
+            field: 'orderStatus',
+            label: '订单状态',
+            component: 'Select',
+            colProps: {
+              xl: 5,
+              xxl: 5,
+            },
+            componentProps: {
+              options: [
+                {
+                  label: '全部订单',
+                  value: '',
+                  key: '1',
+                },
+                {
+                  label: '待付款',
+                  value: '0',
+                  key: '2',
+                },
+                {
+                  label: '订单已取消',
+                  value: '101',
+                  key: '3',
+                },
+                {
+                  label: '订单已付款',
+                  value: '201',
+                  key: '4',
+                },
+                {
+                  label: '订单已发货',
+                  value: '1',
+                  key: '5',
+                },
+                {
+                  label: '订单已收货',
+                  value: '2',
+                  key: '6',
+                },
+                {
+                  label: '完成',
+                  value: '501',
+                  key: '7',
+                },
+              ],
+            },
+          },
+          {
+            field: 'brandId',
+            label: 'VR场景',
+            component: 'ApiSelect',
+            componentProps: {
+              api: BrandListApi,
+              // resultField: 'list',
+              numberToString: true,
+              labelField: 'name',
+              valueField: 'id',
+              immediate: true,
+              params: {
+                page: 1,
+                limit: 1000,
+              },
+            },
+            colProps: {
+              xl: 5,
+              xxl: 5,
+            },
+          },
+        ],
+      };
+
+      const getCheckboxProps = (record: Recordable) => {
+        console.log('record', record);
+        return { disabled: record.type == 2 };
+      };
+      const [registerConfirmModal, { openModal: openConfirmModal }] = useModal();
+
+      const [registerPrintModal, { openModal: openPrintModal }] = useModal();
+
+      const [registerTable, { getSelectRows, reload }] = useTable({
+        title: '订单列表',
+        api: ListApi,
+        columns: columns,
+        useSearchForm: true,
+        formConfig: searchForm,
+        clickToRowSelect: false,
+        showTableSetting: true,
+        tableSetting: { fullScreen: true },
+        showIndexColumn: false,
+        rowKey: 'id',
+        pagination: { pageSize: 20 },
+        ellipsis: false,
+        fetchSetting: {
+          pageField: 'page',
+          sizeField: 'limit',
+          listField: 'list',
+          totalField: 'totalCount',
+        },
+        bordered: true,
+        sortFn: (sortInfo) => {
+          let order = sortInfo.order && sortInfo.order.replace('end', '');
+          return { ...sortInfo, sidx: sortInfo.field, order: order };
+        },
+      });
+
+      function renderOrderTypeLabel(type: number): string {
+        switch (type) {
+          case 0:
+            return '立即购买';
+          case 1:
+            return '延后购买';
+          default:
+            return '立即购买';
+        }
+      }
+      function renderOrderStatusLabel(type: number): string {
+        switch (type) {
+          case 0:
+            return '待付款';
+          case 101:
+            return '已取消';
+          case 102:
+            return '已删除';
+          case 201:
+            return '已付款';
+          case 300:
+            return '已发货';
+          case 301:
+            return '已收货';
+          case 401:
+            return '退款';
+          case 402:
+            return '退款退货';
+          case 501:
+            return '完成';
+          default:
+            return '';
+        }
+      }
+      function rendershippingStatusLabel(type: number): string {
+        switch (type) {
+          case 0:
+            return '未发货';
+          case 1:
+            return '已发货';
+          default:
+            return '';
+        }
+      }
+      function renderpayStatusLabel(type: number): string {
+        switch (type) {
+          case 0:
+            return '未付款';
+          case 1:
+            return '已付款';
+          default:
+            return '已付款';
+        }
+      }
+      // function onSelectChange(selectedRowKeys: (string | number)[]) {
+      //   console.log(selectedRowKeys);
+      //   // checkedKeys.value = selectedRowKeys;
+      // }
+      function sendPackage(record: Recordable) {
+        openConfirmModal(true, record);
+      }
+      async function handleConfirmReceive(record: Recordable) {
+        const { id, brandId } = record;
+        let res = await confirmOrder({
+          id,
+          brandId,
+        });
+        if (res) {
+          reload();
+          createMessage.success('操作成功');
+        }
+      }
+      async function printDetail(record: Recordable) {
+        const orderInfo = await GetOrderInfoApi({
+          id: record.id,
+          brandId: record.brandId,
+        });
+        console.log('orderInfo', orderInfo);
+        openPrintModal(true, orderInfo);
+      }
+      async function exportExcel() {
+        const rows = getSelectRows();
+        if (rows.length === 0) {
+          createMessage.info(t('modal.atLeastOne'));
+          return;
+        }
+
+        const data = rows.reduce((pre, current) => pre.concat(current['orderSn']), []).join(',');
+        console.log('data', data);
+        const res = await exportExcelApi({
+          orderSnList: data,
+        });
+        if (res.url) {
+          downloadByUrl({
+            url: res.url,
+            target: '_self',
+          });
+        }
+      }
+
+      return {
+        registerTable,
+        createMessage,
+        t,
+        go,
+        renderOrderTypeLabel,
+        renderOrderStatusLabel,
+        rendershippingStatusLabel,
+        renderpayStatusLabel,
+        sendPackage,
+        // onSelectChange,
+        handleConfirmReceive,
+        registerConfirmModal,
+        reload,
+        printDetail,
+        registerPrintModal,
+        exportExcel,
+        getCheckboxProps,
+      };
+    },
+  });
+</script>

+ 148 - 0
src/views/order/printModal.vue

@@ -0,0 +1,148 @@
+<template>
+  <BasicModal v-bind="$attrs" @register="register" title="打印票据" width="80%">
+    <BasicTable @register="registerTable" id="printTable">
+      <template #tableTitle>
+        <div class="pb-30px pl-15px">
+          <address>
+            <div
+              ><strong>{{ modalRecord.consignee }}</strong></div
+            >
+            <div>{{ modalRecord.address }}</div>
+            <abbr title="phone"> 联系方式: </abbr> {{ modalRecord.mobile }}
+          </address>
+        </div>
+      </template>
+
+      <template #toolbar>
+        <div class="pb-30px pl-15px">
+          <div
+            ><strong>单据编号 :</strong>
+            <span style="color: #1ab394">{{ modalRecord.orderSn }}</span></div
+          >
+          <div><strong>日期 :</strong> : <Time :value="modalRecord.addTime" mode="datetime" /></div>
+        </div>
+      </template>
+      <template #footer>
+        <div style="text-align: right; min-width: 100px"> 总计:¥{{ modalRecord.goodsPrice }} </div>
+      </template>
+      <template #retailPrice="{ record }">
+        <div>¥{{ record.retailPrice }} </div>
+      </template>
+      <template #marketPrice="{ record }">
+        <div>¥{{ record.number * record.retailPrice }} </div>
+      </template>
+    </BasicTable>
+
+    <template #footer>
+      <a-button type="primary" @click="handlePrint" class="mr-2">打印</a-button>
+    </template>
+  </BasicModal>
+</template>
+<script lang="ts">
+  // reactive
+  import { defineComponent, nextTick, reactive } from 'vue';
+  import { BasicTable, useTable, BasicColumn } from '/@/components/Table';
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import { cloneDeep } from 'lodash-es';
+  import { Time } from '/@/components/Time';
+  import printJS from 'print-js';
+  export default defineComponent({
+    name: 'PrintModal',
+    components: { BasicModal, BasicTable, Time },
+    emits: ['success', 'register', 'reload'],
+    setup() {
+      // const isUpdate = ref(true);
+      let modalRecord = reactive({
+        id: 0,
+        address: '',
+        consignee: '',
+        mobile: '',
+        orderSn: '',
+        addTime: '',
+        goodsPrice: '',
+      });
+      const columns: BasicColumn[] = [
+        {
+          title: '清单',
+          dataIndex: 'goodsName',
+          width: 180,
+        },
+        {
+          title: '数量',
+          dataIndex: 'number',
+          ellipsis: false,
+          width: 180,
+        },
+        {
+          title: '单价',
+          dataIndex: 'retailPrice',
+          ellipsis: false,
+          width: 180,
+          slots: { customRender: 'retailPrice' },
+        },
+        {
+          title: '总价',
+          dataIndex: 'totalPrice',
+          ellipsis: false,
+          width: 180,
+          slots: { customRender: 'marketPrice' },
+        },
+      ];
+
+      const [registerTable, { setTableData, redoHeight }] = useTable({
+        title: '订单列表',
+
+        columns: columns,
+        // useSearchForm: false,
+        // formConfig: searchForm,
+        // clickToRowSelect: false,
+        // showTableSetting: true,
+        // tableSetting: { fullScreen: true },
+        showIndexColumn: false,
+        // maxHeight: 250,
+        // resizeHeightOffset: 0,
+        isCanResizeParent: true,
+        // rowKey: 'id',
+        pagination: false,
+
+        bordered: true,
+      });
+
+      const [register, { closeModal }] = useModalInner((data) => {
+        data && onDataReceive(data);
+      });
+      function onDataReceive(data) {
+        console.log('data', data);
+        // modalRecord = { ...cloneDeep(data) };
+        // console.log('modalRecord', modalRecord);
+        modalRecord.consignee = data.consignee;
+        modalRecord.address = data.address;
+        modalRecord.mobile = data.mobile;
+        modalRecord.orderSn = data.orderSn;
+        modalRecord.addTime = data.addTime;
+        modalRecord.goodsPrice = data.goodsPrice;
+        // modalRecord.brandId = data.brandId;
+        nextTick(() => {
+          setTableData(cloneDeep(data.goodsList));
+          redoHeight();
+        });
+      }
+      async function handlePrint() {
+        printJS({
+          printable: 'printTable',
+          type: 'html',
+          style: 'table th td { border: 1px solid black; border-collapse: collapse; }  }',
+        });
+      }
+
+      return {
+        register,
+
+        closeModal,
+        registerTable,
+        modalRecord,
+        handlePrint,
+      };
+    },
+  });
+</script>

+ 5 - 4
src/views/sys/login/LoginForm.vue

@@ -119,8 +119,8 @@
   const rememberMe = ref(false);
 
   const formData = reactive({
-    account: 'vben',
-    password: '123456',
+    account: 'super-admin',
+    password: 'Aa123456',
   });
 
   const { validForm } = useFormValid(formRef);
@@ -136,13 +136,14 @@
       loading.value = true;
       const userInfo = await userStore.login({
         password: data.password,
-        username: data.account,
+        userName: data.account,
         mode: 'none', //不要默认的错误提示
       });
+      console.log('userInfo',userInfo)
       if (userInfo) {
         notification.success({
           message: t('sys.login.loginSuccessTitle'),
-          description: `${t('sys.login.loginSuccessDesc')}: ${userInfo.realName}`,
+          description: `${t('sys.login.loginSuccessDesc')}: ${userInfo.userName}`,
           duration: 3,
         });
       }

+ 62 - 0
src/views/system/account/AccountDetail.vue

@@ -0,0 +1,62 @@
+<template>
+  <PageWrapper
+    :title="`用户` + userId + `的资料`"
+    content="这是用户资料详情页面。本页面仅用于演示相同路由在tab中打开多个页面并且显示不同的数据"
+    contentBackground
+    @back="goBack"
+  >
+    <template #extra>
+      <a-button type="primary" danger> 禁用账号 </a-button>
+      <a-button type="primary"> 修改密码 </a-button>
+    </template>
+    <template #footer>
+      <a-tabs default-active-key="detail" v-model:activeKey="currentKey">
+        <a-tab-pane key="detail" tab="用户资料" />
+        <a-tab-pane key="logs" tab="操作日志" />
+      </a-tabs>
+    </template>
+    <div class="pt-4 m-4 desc-wrap">
+      <template v-if="currentKey == 'detail'">
+        <div v-for="i in 10" :key="i">这是用户{{ userId }}资料Tab</div>
+      </template>
+      <template v-if="currentKey == 'logs'">
+        <div v-for="i in 10" :key="i">这是用户{{ userId }}操作日志Tab</div>
+      </template>
+    </div>
+  </PageWrapper>
+</template>
+
+<script>
+  import { defineComponent, ref } from 'vue';
+  import { useRoute } from 'vue-router';
+  import { PageWrapper } from '/@/components/Page';
+  import { useGo } from '/@/hooks/web/usePage';
+  import { useTabs } from '/@/hooks/web/useTabs';
+  import { Tabs } from 'ant-design-vue';
+  export default defineComponent({
+    name: 'AccountDetail',
+    components: { PageWrapper, ATabs: Tabs, ATabPane: Tabs.TabPane },
+    setup() {
+      const route = useRoute();
+      const go = useGo();
+      // 此处可以得到用户ID
+      const userId = ref(route.params?.id);
+      const currentKey = ref('detail');
+      const { setTitle } = useTabs();
+      // TODO
+      // 本页代码仅作演示,实际应当通过userId从接口获得用户的相关资料
+
+      // 设置Tab的标题(不会影响页面标题)
+      setTitle('详情:用户' + userId.value);
+
+      // 页面左侧点击返回链接时的操作
+      function goBack() {
+        // 本例的效果时点击返回始终跳转到账号列表页,实际应用时可返回上一页
+        go('/system/account');
+      }
+      return { userId, currentKey, goBack };
+    },
+  });
+</script>
+
+<style></style>

+ 74 - 0
src/views/system/account/AccountModal.vue

@@ -0,0 +1,74 @@
+<template>
+  <BasicModal v-bind="$attrs" @register="registerModal" :title="getTitle" @ok="handleSubmit">
+    <BasicForm @register="registerForm" />
+  </BasicModal>
+</template>
+<script lang="ts">
+  import { defineComponent, ref, computed, unref } from 'vue';
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import { BasicForm, useForm } from '/@/components/Form/index';
+  import { accountFormSchema } from './account.data';
+  import { getDeptList } from '/@/api/demo/system';
+
+  export default defineComponent({
+    name: 'AccountModal',
+    components: { BasicModal, BasicForm },
+    emits: ['success', 'register'],
+    setup(_, { emit }) {
+      const isUpdate = ref(true);
+      const rowId = ref('');
+
+      const [registerForm, { setFieldsValue, updateSchema, resetFields, validate }] = useForm({
+        labelWidth: 100,
+        baseColProps: { span: 24 },
+        schemas: accountFormSchema,
+        showActionButtonGroup: false,
+        actionColOptions: {
+          span: 23,
+        },
+      });
+
+      const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
+        resetFields();
+        setModalProps({ confirmLoading: false });
+        isUpdate.value = !!data?.isUpdate;
+
+        if (unref(isUpdate)) {
+          rowId.value = data.record.id;
+          setFieldsValue({
+            ...data.record,
+          });
+        }
+
+        const treeData = await getDeptList();
+        updateSchema([
+          {
+            field: 'pwd',
+            show: !unref(isUpdate),
+          },
+          {
+            field: 'dept',
+            componentProps: { treeData },
+          },
+        ]);
+      });
+
+      const getTitle = computed(() => (!unref(isUpdate) ? '新增账号' : '编辑账号'));
+
+      async function handleSubmit() {
+        try {
+          const values = await validate();
+          setModalProps({ confirmLoading: true });
+          // TODO custom api
+          console.log(values);
+          closeModal();
+          emit('success', { isUpdate: unref(isUpdate), values: { ...values, id: rowId.value } });
+        } finally {
+          setModalProps({ confirmLoading: false });
+        }
+      }
+
+      return { registerModal, registerForm, getTitle, handleSubmit };
+    },
+  });
+</script>

+ 42 - 0
src/views/system/account/DeptTree.vue

@@ -0,0 +1,42 @@
+<template>
+  <div class="m-4 mr-0 overflow-hidden bg-white">
+    <BasicTree
+      title="部门列表"
+      toolbar
+      search
+      :clickRowToExpand="false"
+      :treeData="treeData"
+      :fieldNames="{ key: 'id', title: 'deptName' }"
+      @select="handleSelect"
+    />
+  </div>
+</template>
+<script lang="ts">
+  import { defineComponent, onMounted, ref } from 'vue';
+
+  import { BasicTree, TreeItem } from '/@/components/Tree';
+  import { getDeptList } from '/@/api/demo/system';
+
+  export default defineComponent({
+    name: 'DeptTree',
+    components: { BasicTree },
+
+    emits: ['select'],
+    setup(_, { emit }) {
+      const treeData = ref<TreeItem[]>([]);
+
+      async function fetch() {
+        treeData.value = (await getDeptList()) as unknown as TreeItem[];
+      }
+
+      function handleSelect(keys) {
+        emit('select', keys[0]);
+      }
+
+      onMounted(() => {
+        fetch();
+      });
+      return { treeData, handleSelect };
+    },
+  });
+</script>

+ 127 - 0
src/views/system/account/account.data.ts

@@ -0,0 +1,127 @@
+import { getAllRoleList, isAccountExist } from '/@/api/demo/system';
+import { BasicColumn } from '/@/components/Table';
+import { FormSchema } from '/@/components/Table';
+
+export const columns: BasicColumn[] = [
+  {
+    title: '用户名',
+    dataIndex: 'account',
+    width: 120,
+  },
+  {
+    title: '昵称',
+    dataIndex: 'nickname',
+    width: 120,
+  },
+  {
+    title: '邮箱',
+    dataIndex: 'email',
+    width: 120,
+  },
+  {
+    title: '创建时间',
+    dataIndex: 'createTime',
+    width: 180,
+  },
+  {
+    title: '角色',
+    dataIndex: 'role',
+    width: 200,
+  },
+  {
+    title: '备注',
+    dataIndex: 'remark',
+  },
+];
+
+export const searchFormSchema: FormSchema[] = [
+  {
+    field: 'account',
+    label: '用户名',
+    component: 'Input',
+    colProps: { span: 8 },
+  },
+  {
+    field: 'nickname',
+    label: '昵称',
+    component: 'Input',
+    colProps: { span: 8 },
+  },
+];
+
+export const accountFormSchema: FormSchema[] = [
+  {
+    field: 'account',
+    label: '用户名',
+    component: 'Input',
+    helpMessage: ['本字段演示异步验证', '不能输入带有admin的用户名'],
+    rules: [
+      {
+        required: true,
+        message: '请输入用户名',
+      },
+      {
+        validator(_, value) {
+          return new Promise((resolve, reject) => {
+            isAccountExist(value)
+              .then(() => resolve())
+              .catch((err) => {
+                reject(err.message || '验证失败');
+              });
+          });
+        },
+      },
+    ],
+  },
+  {
+    field: 'pwd',
+    label: '密码',
+    component: 'InputPassword',
+    required: true,
+    ifShow: false,
+  },
+  {
+    label: '角色',
+    field: 'role',
+    component: 'ApiSelect',
+    componentProps: {
+      api: getAllRoleList,
+      labelField: 'roleName',
+      valueField: 'roleValue',
+    },
+    required: true,
+  },
+  {
+    field: 'dept',
+    label: '所属部门',
+    component: 'TreeSelect',
+    componentProps: {
+      fieldNames: {
+        label: 'deptName',
+        key: 'id',
+        value: 'id',
+      },
+      getPopupContainer: () => document.body,
+    },
+    required: true,
+  },
+  {
+    field: 'nickname',
+    label: '昵称',
+    component: 'Input',
+    required: true,
+  },
+
+  {
+    label: '邮箱',
+    field: 'email',
+    component: 'Input',
+    required: true,
+  },
+
+  {
+    label: '备注',
+    field: 'remark',
+    component: 'InputTextArea',
+  },
+];

+ 137 - 0
src/views/system/account/index.vue

@@ -0,0 +1,137 @@
+<template>
+  <PageWrapper dense contentFullHeight fixedHeight contentClass="flex">
+    <DeptTree class="w-1/4 xl:w-1/5" @select="handleSelect" />
+    <BasicTable @register="registerTable" class="w-3/4 xl:w-4/5" :searchInfo="searchInfo">
+      <template #toolbar>
+        <a-button type="primary" @click="handleCreate">新增账号</a-button>
+      </template>
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'action'">
+          <TableAction
+            :actions="[
+              {
+                icon: 'clarity:info-standard-line',
+                tooltip: '查看用户详情',
+                onClick: handleView.bind(null, record),
+              },
+              {
+                icon: 'clarity:note-edit-line',
+                tooltip: '编辑用户资料',
+                onClick: handleEdit.bind(null, record),
+              },
+              {
+                icon: 'ant-design:delete-outlined',
+                color: 'error',
+                tooltip: '删除此账号',
+                popConfirm: {
+                  title: '是否确认删除',
+                  placement: 'left',
+                  confirm: handleDelete.bind(null, record),
+                },
+              },
+            ]"
+          />
+        </template>
+      </template>
+    </BasicTable>
+    <AccountModal @register="registerModal" @success="handleSuccess" />
+  </PageWrapper>
+</template>
+<script lang="ts">
+  import { defineComponent, reactive } from 'vue';
+
+  import { BasicTable, useTable, TableAction } from '/@/components/Table';
+  import { getAccountList } from '/@/api/demo/system';
+  import { PageWrapper } from '/@/components/Page';
+  import DeptTree from './DeptTree.vue';
+
+  import { useModal } from '/@/components/Modal';
+  import AccountModal from './AccountModal.vue';
+
+  import { columns, searchFormSchema } from './account.data';
+  import { useGo } from '/@/hooks/web/usePage';
+
+  export default defineComponent({
+    name: 'AccountManagement',
+    components: { BasicTable, PageWrapper, DeptTree, AccountModal, TableAction },
+    setup() {
+      const go = useGo();
+      const [registerModal, { openModal }] = useModal();
+      const searchInfo = reactive<Recordable>({});
+      const [registerTable, { reload, updateTableDataRecord }] = useTable({
+        title: '账号列表',
+        api: getAccountList,
+        rowKey: 'id',
+        columns,
+        formConfig: {
+          labelWidth: 120,
+          schemas: searchFormSchema,
+          autoSubmitOnEnter: true,
+        },
+        useSearchForm: true,
+        showTableSetting: true,
+        bordered: true,
+        handleSearchInfoFn(info) {
+          console.log('handleSearchInfoFn', info);
+          return info;
+        },
+        actionColumn: {
+          width: 120,
+          title: '操作',
+          dataIndex: 'action',
+          // slots: { customRender: 'action' },
+        },
+      });
+
+      function handleCreate() {
+        openModal(true, {
+          isUpdate: false,
+        });
+      }
+
+      function handleEdit(record: Recordable) {
+        console.log(record);
+        openModal(true, {
+          record,
+          isUpdate: true,
+        });
+      }
+
+      function handleDelete(record: Recordable) {
+        console.log(record);
+      }
+
+      function handleSuccess({ isUpdate, values }) {
+        if (isUpdate) {
+          // 演示不刷新表格直接更新内部数据。
+          // 注意:updateTableDataRecord要求表格的rowKey属性为string并且存在于每一行的record的keys中
+          const result = updateTableDataRecord(values.id, values);
+          console.log(result);
+        } else {
+          reload();
+        }
+      }
+
+      function handleSelect(deptId = '') {
+        searchInfo.deptId = deptId;
+        reload();
+      }
+
+      function handleView(record: Recordable) {
+        go('/system/account_detail/' + record.id);
+      }
+
+      return {
+        registerTable,
+        registerModal,
+        handleCreate,
+        handleEdit,
+        handleDelete,
+        handleSuccess,
+        handleSelect,
+        handleView,
+        searchInfo,
+      };
+    },
+  });
+</script>

+ 62 - 0
src/views/system/dept/DeptModal.vue

@@ -0,0 +1,62 @@
+<template>
+  <BasicModal v-bind="$attrs" @register="registerModal" :title="getTitle" @ok="handleSubmit">
+    <BasicForm @register="registerForm" />
+  </BasicModal>
+</template>
+<script lang="ts">
+  import { defineComponent, ref, computed, unref } from 'vue';
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import { BasicForm, useForm } from '/@/components/Form/index';
+  import { formSchema } from './dept.data';
+
+  import { getDeptList } from '/@/api/demo/system';
+  export default defineComponent({
+    name: 'DeptModal',
+    components: { BasicModal, BasicForm },
+    emits: ['success', 'register'],
+    setup(_, { emit }) {
+      const isUpdate = ref(true);
+
+      const [registerForm, { resetFields, setFieldsValue, updateSchema, validate }] = useForm({
+        labelWidth: 100,
+        baseColProps: { span: 24 },
+        schemas: formSchema,
+        showActionButtonGroup: false,
+      });
+
+      const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
+        resetFields();
+        setModalProps({ confirmLoading: false });
+        isUpdate.value = !!data?.isUpdate;
+
+        if (unref(isUpdate)) {
+          setFieldsValue({
+            ...data.record,
+          });
+        }
+        const treeData = await getDeptList();
+        updateSchema({
+          field: 'parentDept',
+          componentProps: { treeData },
+        });
+      });
+
+      const getTitle = computed(() => (!unref(isUpdate) ? '新增部门' : '编辑部门'));
+
+      async function handleSubmit() {
+        try {
+          const values = await validate();
+          setModalProps({ confirmLoading: true });
+          // TODO custom api
+          console.log(values);
+          closeModal();
+          emit('success');
+        } finally {
+          setModalProps({ confirmLoading: false });
+        }
+      }
+
+      return { registerModal, registerForm, getTitle, handleSubmit };
+    },
+  });
+</script>

+ 108 - 0
src/views/system/dept/dept.data.ts

@@ -0,0 +1,108 @@
+import { BasicColumn } from '/@/components/Table';
+import { FormSchema } from '/@/components/Table';
+import { h } from 'vue';
+import { Tag } from 'ant-design-vue';
+
+export const columns: BasicColumn[] = [
+  {
+    title: '部门名称',
+    dataIndex: 'deptName',
+    width: 160,
+    align: 'left',
+  },
+  {
+    title: '排序',
+    dataIndex: 'orderNo',
+    width: 50,
+  },
+  {
+    title: '状态',
+    dataIndex: 'status',
+    width: 80,
+    customRender: ({ record }) => {
+      const status = record.status;
+      const enable = ~~status === 0;
+      const color = enable ? 'green' : 'red';
+      const text = enable ? '启用' : '停用';
+      return h(Tag, { color: color }, () => text);
+    },
+  },
+  {
+    title: '创建时间',
+    dataIndex: 'createTime',
+    width: 180,
+  },
+  {
+    title: '备注',
+    dataIndex: 'remark',
+  },
+];
+
+export const searchFormSchema: FormSchema[] = [
+  {
+    field: 'deptName',
+    label: '部门名称',
+    component: 'Input',
+    colProps: { span: 8 },
+  },
+  {
+    field: 'status',
+    label: '状态',
+    component: 'Select',
+    componentProps: {
+      options: [
+        { label: '启用', value: '0' },
+        { label: '停用', value: '1' },
+      ],
+    },
+    colProps: { span: 8 },
+  },
+];
+
+export const formSchema: FormSchema[] = [
+  {
+    field: 'deptName',
+    label: '部门名称',
+    component: 'Input',
+    required: true,
+  },
+  {
+    field: 'parentDept',
+    label: '上级部门',
+    component: 'TreeSelect',
+
+    componentProps: {
+      fieldNames: {
+        label: 'deptName',
+        key: 'id',
+        value: 'id',
+      },
+      getPopupContainer: () => document.body,
+    },
+    required: true,
+  },
+  {
+    field: 'orderNo',
+    label: '排序',
+    component: 'InputNumber',
+    required: true,
+  },
+  {
+    field: 'status',
+    label: '状态',
+    component: 'RadioButtonGroup',
+    defaultValue: '0',
+    componentProps: {
+      options: [
+        { label: '启用', value: '0' },
+        { label: '停用', value: '1' },
+      ],
+    },
+    required: true,
+  },
+  {
+    label: '备注',
+    field: 'remark',
+    component: 'InputTextArea',
+  },
+];

+ 103 - 0
src/views/system/dept/index.vue

@@ -0,0 +1,103 @@
+<template>
+  <div>
+    <BasicTable @register="registerTable">
+      <template #toolbar>
+        <a-button type="primary" @click="handleCreate"> 新增部门 </a-button>
+      </template>
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'action'">
+          <TableAction
+            :actions="[
+              {
+                icon: 'clarity:note-edit-line',
+                onClick: handleEdit.bind(null, record),
+              },
+              {
+                icon: 'ant-design:delete-outlined',
+                color: 'error',
+                popConfirm: {
+                  title: '是否确认删除',
+                  placement: 'left',
+                  confirm: handleDelete.bind(null, record),
+                },
+              },
+            ]"
+          />
+        </template>
+      </template>
+    </BasicTable>
+    <DeptModal @register="registerModal" @success="handleSuccess" />
+  </div>
+</template>
+<script lang="ts">
+  import { defineComponent } from 'vue';
+
+  import { BasicTable, useTable, TableAction } from '/@/components/Table';
+  import { getDeptList } from '/@/api/demo/system';
+
+  import { useModal } from '/@/components/Modal';
+  import DeptModal from './DeptModal.vue';
+
+  import { columns, searchFormSchema } from './dept.data';
+
+  export default defineComponent({
+    name: 'DeptManagement',
+    components: { BasicTable, DeptModal, TableAction },
+    setup() {
+      const [registerModal, { openModal }] = useModal();
+      const [registerTable, { reload }] = useTable({
+        title: '部门列表',
+        api: getDeptList,
+        columns,
+        formConfig: {
+          labelWidth: 120,
+          schemas: searchFormSchema,
+        },
+        pagination: false,
+        striped: false,
+        useSearchForm: true,
+        showTableSetting: true,
+        bordered: true,
+        showIndexColumn: false,
+        canResize: false,
+        actionColumn: {
+          width: 80,
+          title: '操作',
+          dataIndex: 'action',
+          // slots: { customRender: 'action' },
+          fixed: undefined,
+        },
+      });
+
+      function handleCreate() {
+        openModal(true, {
+          isUpdate: false,
+        });
+      }
+
+      function handleEdit(record: Recordable) {
+        openModal(true, {
+          record,
+          isUpdate: true,
+        });
+      }
+
+      function handleDelete(record: Recordable) {
+        console.log(record);
+      }
+
+      function handleSuccess() {
+        reload();
+      }
+
+      return {
+        registerTable,
+        registerModal,
+        handleCreate,
+        handleEdit,
+        handleDelete,
+        handleSuccess,
+      };
+    },
+  });
+</script>

+ 70 - 0
src/views/system/menu/MenuDrawer.vue

@@ -0,0 +1,70 @@
+<template>
+  <BasicDrawer
+    v-bind="$attrs"
+    @register="registerDrawer"
+    showFooter
+    :title="getTitle"
+    width="50%"
+    @ok="handleSubmit"
+  >
+    <BasicForm @register="registerForm" />
+  </BasicDrawer>
+</template>
+<script lang="ts">
+  import { defineComponent, ref, computed, unref } from 'vue';
+  import { BasicForm, useForm } from '/@/components/Form/index';
+  import { formSchema } from './menu.data';
+  import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
+
+  import { getMenuList } from '/@/api/demo/system';
+
+  export default defineComponent({
+    name: 'MenuDrawer',
+    components: { BasicDrawer, BasicForm },
+    emits: ['success', 'register'],
+    setup(_, { emit }) {
+      const isUpdate = ref(true);
+
+      const [registerForm, { resetFields, setFieldsValue, updateSchema, validate }] = useForm({
+        labelWidth: 100,
+        schemas: formSchema,
+        showActionButtonGroup: false,
+        baseColProps: { lg: 12, md: 24 },
+      });
+
+      const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
+        resetFields();
+        setDrawerProps({ confirmLoading: false });
+        isUpdate.value = !!data?.isUpdate;
+
+        if (unref(isUpdate)) {
+          setFieldsValue({
+            ...data.record,
+          });
+        }
+        const treeData = await getMenuList();
+        updateSchema({
+          field: 'parentMenu',
+          componentProps: { treeData },
+        });
+      });
+
+      const getTitle = computed(() => (!unref(isUpdate) ? '新增菜单' : '编辑菜单'));
+
+      async function handleSubmit() {
+        try {
+          const values = await validate();
+          setDrawerProps({ confirmLoading: true });
+          // TODO custom api
+          console.log(values);
+          closeDrawer();
+          emit('success');
+        } finally {
+          setDrawerProps({ confirmLoading: false });
+        }
+      }
+
+      return { registerDrawer, registerForm, getTitle, handleSubmit };
+    },
+  });
+</script>

+ 110 - 0
src/views/system/menu/index.vue

@@ -0,0 +1,110 @@
+<template>
+  <div>
+    <BasicTable @register="registerTable" @fetch-success="onFetchSuccess">
+      <template #toolbar>
+        <a-button type="primary" @click="handleCreate"> 新增菜单 </a-button>
+      </template>
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'action'">
+          <TableAction
+            :actions="[
+              {
+                icon: 'clarity:note-edit-line',
+                onClick: handleEdit.bind(null, record),
+              },
+              {
+                icon: 'ant-design:delete-outlined',
+                color: 'error',
+                popConfirm: {
+                  title: '是否确认删除',
+                  placement: 'left',
+                  confirm: handleDelete.bind(null, record),
+                },
+              },
+            ]"
+          />
+        </template>
+      </template>
+    </BasicTable>
+    <MenuDrawer @register="registerDrawer" @success="handleSuccess" />
+  </div>
+</template>
+<script lang="ts">
+  import { defineComponent, nextTick } from 'vue';
+
+  import { BasicTable, useTable, TableAction } from '/@/components/Table';
+  import { getMenuList } from '/@/api/demo/system';
+
+  import { useDrawer } from '/@/components/Drawer';
+  import MenuDrawer from './MenuDrawer.vue';
+
+  import { columns, searchFormSchema } from './menu.data';
+
+  export default defineComponent({
+    name: 'MenuManagement',
+    components: { BasicTable, MenuDrawer, TableAction },
+    setup() {
+      const [registerDrawer, { openDrawer }] = useDrawer();
+      const [registerTable, { reload, expandAll }] = useTable({
+        title: '菜单列表',
+        api: getMenuList,
+        columns,
+        formConfig: {
+          labelWidth: 120,
+          schemas: searchFormSchema,
+        },
+        isTreeTable: true,
+        pagination: false,
+        striped: false,
+        useSearchForm: true,
+        showTableSetting: true,
+        bordered: true,
+        showIndexColumn: false,
+        canResize: false,
+        actionColumn: {
+          width: 80,
+          title: '操作',
+          dataIndex: 'action',
+          // slots: { customRender: 'action' },
+          fixed: undefined,
+        },
+      });
+
+      function handleCreate() {
+        openDrawer(true, {
+          isUpdate: false,
+        });
+      }
+
+      function handleEdit(record: Recordable) {
+        openDrawer(true, {
+          record,
+          isUpdate: true,
+        });
+      }
+
+      function handleDelete(record: Recordable) {
+        console.log(record);
+      }
+
+      function handleSuccess() {
+        reload();
+      }
+
+      function onFetchSuccess() {
+        // 演示默认展开所有表项
+        nextTick(expandAll);
+      }
+
+      return {
+        registerTable,
+        registerDrawer,
+        handleCreate,
+        handleEdit,
+        handleDelete,
+        handleSuccess,
+        onFetchSuccess,
+      };
+    },
+  });
+</script>

+ 202 - 0
src/views/system/menu/menu.data.ts

@@ -0,0 +1,202 @@
+import { BasicColumn } from '/@/components/Table';
+import { FormSchema } from '/@/components/Table';
+import { h } from 'vue';
+import { Tag } from 'ant-design-vue';
+import { Icon } from '/@/components/Icon';
+
+export const columns: BasicColumn[] = [
+  {
+    title: '菜单名称',
+    dataIndex: 'menuName',
+    width: 200,
+    align: 'left',
+  },
+  {
+    title: '图标',
+    dataIndex: 'icon',
+    width: 50,
+    customRender: ({ record }) => {
+      return h(Icon, { icon: record.icon });
+    },
+  },
+  {
+    title: '权限标识',
+    dataIndex: 'permission',
+    width: 180,
+  },
+  {
+    title: '组件',
+    dataIndex: 'component',
+  },
+  {
+    title: '排序',
+    dataIndex: 'orderNo',
+    width: 50,
+  },
+  {
+    title: '状态',
+    dataIndex: 'status',
+    width: 80,
+    customRender: ({ record }) => {
+      const status = record.status;
+      const enable = ~~status === 0;
+      const color = enable ? 'green' : 'red';
+      const text = enable ? '启用' : '停用';
+      return h(Tag, { color: color }, () => text);
+    },
+  },
+  {
+    title: '创建时间',
+    dataIndex: 'createTime',
+    width: 180,
+  },
+];
+
+const isDir = (type: string) => type === '0';
+const isMenu = (type: string) => type === '1';
+const isButton = (type: string) => type === '2';
+
+export const searchFormSchema: FormSchema[] = [
+  {
+    field: 'menuName',
+    label: '菜单名称',
+    component: 'Input',
+    colProps: { span: 8 },
+  },
+  {
+    field: 'status',
+    label: '状态',
+    component: 'Select',
+    componentProps: {
+      options: [
+        { label: '启用', value: '0' },
+        { label: '停用', value: '1' },
+      ],
+    },
+    colProps: { span: 8 },
+  },
+];
+
+export const formSchema: FormSchema[] = [
+  {
+    field: 'type',
+    label: '菜单类型',
+    component: 'RadioButtonGroup',
+    defaultValue: '0',
+    componentProps: {
+      options: [
+        { label: '目录', value: '0' },
+        { label: '菜单', value: '1' },
+        { label: '按钮', value: '2' },
+      ],
+    },
+    colProps: { lg: 24, md: 24 },
+  },
+  {
+    field: 'menuName',
+    label: '菜单名称',
+    component: 'Input',
+    required: true,
+  },
+
+  {
+    field: 'parentMenu',
+    label: '上级菜单',
+    component: 'TreeSelect',
+    componentProps: {
+      fieldNames: {
+        label: 'menuName',
+        key: 'id',
+        value: 'id',
+      },
+      getPopupContainer: () => document.body,
+    },
+  },
+
+  {
+    field: 'orderNo',
+    label: '排序',
+    component: 'InputNumber',
+    required: true,
+  },
+  {
+    field: 'icon',
+    label: '图标',
+    component: 'IconPicker',
+    required: true,
+    ifShow: ({ values }) => !isButton(values.type),
+  },
+
+  {
+    field: 'routePath',
+    label: '路由地址',
+    component: 'Input',
+    required: true,
+    ifShow: ({ values }) => !isButton(values.type),
+  },
+  {
+    field: 'component',
+    label: '组件路径',
+    component: 'Input',
+    ifShow: ({ values }) => isMenu(values.type),
+  },
+  {
+    field: 'permission',
+    label: '权限标识',
+    component: 'Input',
+    ifShow: ({ values }) => !isDir(values.type),
+  },
+  {
+    field: 'status',
+    label: '状态',
+    component: 'RadioButtonGroup',
+    defaultValue: '0',
+    componentProps: {
+      options: [
+        { label: '启用', value: '0' },
+        { label: '禁用', value: '1' },
+      ],
+    },
+  },
+  {
+    field: 'isExt',
+    label: '是否外链',
+    component: 'RadioButtonGroup',
+    defaultValue: '0',
+    componentProps: {
+      options: [
+        { label: '否', value: '0' },
+        { label: '是', value: '1' },
+      ],
+    },
+    ifShow: ({ values }) => !isButton(values.type),
+  },
+
+  {
+    field: 'keepalive',
+    label: '是否缓存',
+    component: 'RadioButtonGroup',
+    defaultValue: '0',
+    componentProps: {
+      options: [
+        { label: '否', value: '0' },
+        { label: '是', value: '1' },
+      ],
+    },
+    ifShow: ({ values }) => isMenu(values.type),
+  },
+
+  {
+    field: 'show',
+    label: '是否显示',
+    component: 'RadioButtonGroup',
+    defaultValue: '0',
+    componentProps: {
+      options: [
+        { label: '是', value: '0' },
+        { label: '否', value: '1' },
+      ],
+    },
+    ifShow: ({ values }) => !isButton(values.type),
+  },
+];

+ 45 - 0
src/views/system/password/index.vue

@@ -0,0 +1,45 @@
+<template>
+  <PageWrapper title="修改当前用户密码" content="修改成功后会自动退出当前登录!">
+    <div class="py-8 bg-white flex flex-col justify-center items-center">
+      <BasicForm @register="register" />
+      <div class="flex justify-center">
+        <a-button @click="resetFields"> 重置 </a-button>
+        <a-button class="!ml-4" type="primary" @click="handleSubmit"> 确认 </a-button>
+      </div>
+    </div>
+  </PageWrapper>
+</template>
+<script lang="ts">
+  import { defineComponent } from 'vue';
+  import { PageWrapper } from '/@/components/Page';
+  import { BasicForm, useForm } from '/@/components/Form';
+
+  import { formSchema } from './pwd.data';
+  export default defineComponent({
+    name: 'ChangePassword',
+    components: { BasicForm, PageWrapper },
+    setup() {
+      const [register, { validate, resetFields }] = useForm({
+        size: 'large',
+        baseColProps: { span: 24 },
+        labelWidth: 100,
+        showActionButtonGroup: false,
+        schemas: formSchema,
+      });
+
+      async function handleSubmit() {
+        try {
+          const values = await validate();
+          const { passwordOld, passwordNew } = values;
+
+          // TODO custom api
+          console.log(passwordOld, passwordNew);
+          // const { router } = useRouter();
+          // router.push(pageEnum.BASE_LOGIN);
+        } catch (error) {}
+      }
+
+      return { register, resetFields, handleSubmit };
+    },
+  });
+</script>

+ 46 - 0
src/views/system/password/pwd.data.ts

@@ -0,0 +1,46 @@
+import { FormSchema } from '/@/components/Form';
+
+export const formSchema: FormSchema[] = [
+  {
+    field: 'passwordOld',
+    label: '当前密码',
+    component: 'InputPassword',
+    required: true,
+  },
+  {
+    field: 'passwordNew',
+    label: '新密码',
+    component: 'StrengthMeter',
+    componentProps: {
+      placeholder: '新密码',
+    },
+    rules: [
+      {
+        required: true,
+        message: '请输入新密码',
+      },
+    ],
+  },
+  {
+    field: 'confirmPassword',
+    label: '确认密码',
+    component: 'InputPassword',
+
+    dynamicRules: ({ values }) => {
+      return [
+        {
+          required: true,
+          validator: (_, value) => {
+            if (!value) {
+              return Promise.reject('密码不能为空');
+            }
+            if (value !== values.passwordNew) {
+              return Promise.reject('两次输入的密码不一致!');
+            }
+            return Promise.resolve();
+          },
+        },
+      ];
+    },
+  },
+];

+ 88 - 0
src/views/system/role/RoleDrawer.vue

@@ -0,0 +1,88 @@
+<template>
+  <BasicDrawer
+    v-bind="$attrs"
+    @register="registerDrawer"
+    showFooter
+    :title="getTitle"
+    width="500px"
+    @ok="handleSubmit"
+  >
+    <BasicForm @register="registerForm">
+      <template #menu="{ model, field }">
+        <BasicTree
+          v-model:value="model[field]"
+          :treeData="treeData"
+          :fieldNames="{ title: 'menuName', key: 'id' }"
+          checkable
+          toolbar
+          title="菜单分配"
+        />
+      </template>
+    </BasicForm>
+  </BasicDrawer>
+</template>
+<script lang="ts">
+  import { defineComponent, ref, computed, unref } from 'vue';
+  import { BasicForm, useForm } from '/@/components/Form/index';
+  import { formSchema } from './role.data';
+  import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
+  import { BasicTree, TreeItem } from '/@/components/Tree';
+
+  import { getMenuList } from '/@/api/demo/system';
+
+  export default defineComponent({
+    name: 'RoleDrawer',
+    components: { BasicDrawer, BasicForm, BasicTree },
+    emits: ['success', 'register'],
+    setup(_, { emit }) {
+      const isUpdate = ref(true);
+      const treeData = ref<TreeItem[]>([]);
+
+      const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({
+        labelWidth: 90,
+        baseColProps: { span: 24 },
+        schemas: formSchema,
+        showActionButtonGroup: false,
+      });
+
+      const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
+        resetFields();
+        setDrawerProps({ confirmLoading: false });
+        // 需要在setFieldsValue之前先填充treeData,否则Tree组件可能会报key not exist警告
+        if (unref(treeData).length === 0) {
+          treeData.value = (await getMenuList()) as any as TreeItem[];
+        }
+        isUpdate.value = !!data?.isUpdate;
+
+        if (unref(isUpdate)) {
+          setFieldsValue({
+            ...data.record,
+          });
+        }
+      });
+
+      const getTitle = computed(() => (!unref(isUpdate) ? '新增角色' : '编辑角色'));
+
+      async function handleSubmit() {
+        try {
+          const values = await validate();
+          setDrawerProps({ confirmLoading: true });
+          // TODO custom api
+          console.log(values);
+          closeDrawer();
+          emit('success');
+        } finally {
+          setDrawerProps({ confirmLoading: false });
+        }
+      }
+
+      return {
+        registerDrawer,
+        registerForm,
+        getTitle,
+        handleSubmit,
+        treeData,
+      };
+    },
+  });
+</script>

+ 100 - 0
src/views/system/role/index.vue

@@ -0,0 +1,100 @@
+<template>
+  <div>
+    <BasicTable @register="registerTable">
+      <template #toolbar>
+        <a-button type="primary" @click="handleCreate"> 新增角色 </a-button>
+      </template>
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'action'">
+          <TableAction
+            :actions="[
+              {
+                icon: 'clarity:note-edit-line',
+                onClick: handleEdit.bind(null, record),
+              },
+              {
+                icon: 'ant-design:delete-outlined',
+                color: 'error',
+                popConfirm: {
+                  title: '是否确认删除',
+                  placement: 'left',
+                  confirm: handleDelete.bind(null, record),
+                },
+              },
+            ]"
+          />
+        </template>
+      </template>
+    </BasicTable>
+    <RoleDrawer @register="registerDrawer" @success="handleSuccess" />
+  </div>
+</template>
+<script lang="ts">
+  import { defineComponent } from 'vue';
+
+  import { BasicTable, useTable, TableAction } from '/@/components/Table';
+  import { getRoleListByPage } from '/@/api/demo/system';
+
+  import { useDrawer } from '/@/components/Drawer';
+  import RoleDrawer from './RoleDrawer.vue';
+
+  import { columns, searchFormSchema } from './role.data';
+
+  export default defineComponent({
+    name: 'RoleManagement',
+    components: { BasicTable, RoleDrawer, TableAction },
+    setup() {
+      const [registerDrawer, { openDrawer }] = useDrawer();
+      const [registerTable, { reload }] = useTable({
+        title: '角色列表',
+        api: getRoleListByPage,
+        columns,
+        formConfig: {
+          labelWidth: 120,
+          schemas: searchFormSchema,
+        },
+        useSearchForm: true,
+        showTableSetting: true,
+        bordered: true,
+        showIndexColumn: false,
+        actionColumn: {
+          width: 80,
+          title: '操作',
+          dataIndex: 'action',
+          // slots: { customRender: 'action' },
+          fixed: undefined,
+        },
+      });
+
+      function handleCreate() {
+        openDrawer(true, {
+          isUpdate: false,
+        });
+      }
+
+      function handleEdit(record: Recordable) {
+        openDrawer(true, {
+          record,
+          isUpdate: true,
+        });
+      }
+
+      function handleDelete(record: Recordable) {
+        console.log(record);
+      }
+
+      function handleSuccess() {
+        reload();
+      }
+
+      return {
+        registerTable,
+        registerDrawer,
+        handleCreate,
+        handleEdit,
+        handleDelete,
+        handleSuccess,
+      };
+    },
+  });
+</script>

+ 124 - 0
src/views/system/role/role.data.ts

@@ -0,0 +1,124 @@
+import { BasicColumn } from '/@/components/Table';
+import { FormSchema } from '/@/components/Table';
+import { h } from 'vue';
+import { Switch } from 'ant-design-vue';
+import { setRoleStatus } from '/@/api/demo/system';
+import { useMessage } from '/@/hooks/web/useMessage';
+
+export const columns: BasicColumn[] = [
+  {
+    title: '角色名称',
+    dataIndex: 'roleName',
+    width: 200,
+  },
+  {
+    title: '角色值',
+    dataIndex: 'roleValue',
+    width: 180,
+  },
+  {
+    title: '排序',
+    dataIndex: 'orderNo',
+    width: 50,
+  },
+  {
+    title: '状态',
+    dataIndex: 'status',
+    width: 120,
+    customRender: ({ record }) => {
+      if (!Reflect.has(record, 'pendingStatus')) {
+        record.pendingStatus = false;
+      }
+      return h(Switch, {
+        checked: record.status === '1',
+        checkedChildren: '已启用',
+        unCheckedChildren: '已禁用',
+        loading: record.pendingStatus,
+        onChange(checked: boolean) {
+          record.pendingStatus = true;
+          const newStatus = checked ? '1' : '0';
+          const { createMessage } = useMessage();
+          setRoleStatus(record.id, newStatus)
+            .then(() => {
+              record.status = newStatus;
+              createMessage.success(`已成功修改角色状态`);
+            })
+            .catch(() => {
+              createMessage.error('修改角色状态失败');
+            })
+            .finally(() => {
+              record.pendingStatus = false;
+            });
+        },
+      });
+    },
+  },
+  {
+    title: '创建时间',
+    dataIndex: 'createTime',
+    width: 180,
+  },
+  {
+    title: '备注',
+    dataIndex: 'remark',
+  },
+];
+
+export const searchFormSchema: FormSchema[] = [
+  {
+    field: 'roleNme',
+    label: '角色名称',
+    component: 'Input',
+    colProps: { span: 8 },
+  },
+  {
+    field: 'status',
+    label: '状态',
+    component: 'Select',
+    componentProps: {
+      options: [
+        { label: '启用', value: '0' },
+        { label: '停用', value: '1' },
+      ],
+    },
+    colProps: { span: 8 },
+  },
+];
+
+export const formSchema: FormSchema[] = [
+  {
+    field: 'roleName',
+    label: '角色名称',
+    required: true,
+    component: 'Input',
+  },
+  {
+    field: 'roleValue',
+    label: '角色值',
+    required: true,
+    component: 'Input',
+  },
+  {
+    field: 'status',
+    label: '状态',
+    component: 'RadioButtonGroup',
+    defaultValue: '0',
+    componentProps: {
+      options: [
+        { label: '启用', value: '0' },
+        { label: '停用', value: '1' },
+      ],
+    },
+  },
+  {
+    label: '备注',
+    field: 'remark',
+    component: 'InputTextArea',
+  },
+  {
+    label: ' ',
+    field: 'menu',
+    slot: 'menu',
+    component: 'Input',
+  },
+];

+ 2 - 0
types/axios.d.ts

@@ -36,7 +36,9 @@ export interface Result<T = any> {
   code: number;
   type: 'success' | 'error' | 'warning';
   message: string;
+  error?: string;
   result: T;
+  data: T;
 }
 
 // multipart/form-data: upload file

+ 12 - 0
types/store.d.ts

@@ -40,6 +40,18 @@ export interface UserInfo {
   roles: RoleInfo[];
 }
 
+export interface UserInfoV4 {
+  id: string | number;
+  nickName: string;
+  roleName: string;
+  userName: string;
+  token: string;
+  roleId: number;
+  homePath?: string;
+  avatar?: string;
+  desc?: string;
+}
+
 export interface BeforeMiniState {
   menuCollapsed?: boolean;
   menuSplit?: boolean;