tangning 1 rok temu
rodzic
commit
80ef08c8d4

+ 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","https://testeur.4dkankan.com/service/manage/common/upload/files"],["/service","https://testeur.4dkankan.com"]]
+# VITE_PROXY = [["/basic-api","http://localhost:3000"],["/upload","https://testeur.4dkankan.com/service/manage/common/upload/files"],["/service","https://v4-uat.4dkankan.com"]]
 VITE_PROXY = [["/basic-api","http://localhost:3000"],["/upload","https://v4-uat.4dkankan.com/service/manage/common/upload/files"],["/service","https://v4-uat.4dkankan.com"]]
 
 # Delete console

+ 114 - 0
src/api/retailer/index.ts

@@ -0,0 +1,114 @@
+import { defHttp } from '/@/utils/http/axios';
+import { Result, FileStream, UploadFileParams } from '/#/axios';
+
+enum Api {
+  listApi = '/service/agent/agentManage/list',
+  addApi = '/service/agent/agentManage/add',
+  updateApi = '/service/agent/agentManage/update',
+  addIncrementNum = '/service/agent/agentManage/addIncrementNum',
+  banOrUnBan = '/service/agent/agentManage/banOrUnBan',
+  giveCamera = '/service/agent/camera/giveCamera',
+  giveCameraBatch = '/service/agent/camera/giveCameraBatch',
+  checkUserName = '/service/agent/agentManage/checkUserName',
+  getSubAgent = '/service/agent/agentManage/getSubAgent',
+  downTemplate = '/service/agent/camera/downTemplate',
+}
+
+export const listApi = (params) =>
+  defHttp.post<Result>({
+    url: Api.listApi,
+    params: params,
+    // data: params,
+    headers: {
+      // @ts-ignore
+      ignoreCancelToken: true,
+    },
+  });
+export const addApi = (params) =>
+  defHttp.post<Result>({
+    url: Api.addApi,
+    params: params,
+    // data: params,
+    headers: {
+      // @ts-ignore
+      ignoreCancelToken: true,
+    },
+  });
+export const addIncrementNum = (params) =>
+  defHttp.post<Result>({
+    url: Api.addIncrementNum,
+    params: params,
+    // data: params,
+    headers: {
+      // @ts-ignore
+      ignoreCancelToken: true,
+    },
+  });
+export const giveCamera = (params) =>
+  defHttp.post<Result>({
+    url: Api.giveCamera,
+    params: params,
+    // data: params,
+    headers: {
+      // @ts-ignore
+      ignoreCancelToken: true,
+    },
+  });
+export const updateApi = (params) =>
+  defHttp.post<Result>({
+    url: Api.updateApi,
+    params: params,
+    // data: params,
+    headers: {
+      // @ts-ignore
+      ignoreCancelToken: true,
+    },
+  });
+export const banOrUnBan = (params) =>
+  defHttp.post<Result>({
+    url: Api.banOrUnBan,
+    params: params,
+    // data: params,
+    headers: {
+      // @ts-ignore
+      ignoreCancelToken: true,
+    },
+  });
+
+export const getSubAgent = (params) =>
+  defHttp.get<Result>({
+    url: Api.getSubAgent,
+    params,
+    data: params,
+    headers: {
+      // @ts-ignore
+      ignoreCancelToken: true,
+    },
+  });
+
+export const downTemplate = (params) =>
+  defHttp.downloadFile<FileStream>({
+    method: 'GET',
+    url: Api.downTemplate,
+    params: params,
+    // data: params,
+    fileName: '分销商设备清单模板.xlsx',
+    headers: {
+      // @ts-ignore
+      ignoreCancelToken: true,
+    },
+    responseType: 'blob',
+  });
+
+export function giveCameraBatch(
+  params: UploadFileParams,
+  onUploadProgress: (progressEvent: ProgressEvent) => void,
+) {
+  return defHttp.uploadFile(
+    {
+      url: Api.giveCameraBatch,
+      onUploadProgress,
+    },
+    params,
+  );
+}

+ 2 - 1
src/locales/lang/en/code.ts

@@ -20,7 +20,8 @@ export default {
     '4018': 'Insufficient number of downloads!',
     '4019': 'SN code does not exist!',
     '4020': 'Multiple login attempts detected, please try again in five minutes!',
-    '4021': 'Cannot copy due to insufficient capacity',
+    '4021': 'Distributor name already exists',
+    '4022': 'The distributor account already exists',
     '5005': 'Empty scene',
     '5071': 'Duplicated scenes do not support recalculation.',
     '50009': 'Please upgrade the V3 scene before recalculating.',

+ 5 - 4
src/locales/lang/en/routes/dashboard.ts

@@ -4,9 +4,10 @@ export default {
   workbench: 'Workbench',
   analysis: 'Analysis',
   devices: 'Equipment Management',
-  equity:'Subscription Management',
-  scene:'Scene Management',
-  finance:'Sales Statistics',
+  equity: 'Subscription Management',
+  scene: 'Scene Management',
+  finance: 'Sales Statistics',
   cameraScene: 'Scene Management',
-  loglist:'Operation Log',
+  loglist: 'Operation Log',
+  retailer: 'Distributor',
 };

+ 28 - 0
src/locales/lang/en/routes/retailer.ts

@@ -0,0 +1,28 @@
+export default {
+  name: '分销商名称',
+  add: '新增分销商',
+  setName: '权益设置',
+  nickName: '联系人',
+  userName: '请输入四维看看账号',
+  userId: '账号',
+  tips: '该账号不存在',
+  jxs: '经销商',
+  fxs: '分销商',
+  majorAddNum: '专业会员(年):剩余可售 {value} 年,本次新增 (年数)',
+  highAddNum: '高级会员(月):剩余可售 {value} 年,本次新增 (月数)',
+  downAddNum: '场景下载(次):剩余可售 {value} 年,本次新增 (次数)',
+  ff: '分发',
+  allff: '设备批量分发',
+  majorTotalNum: '剩余专业会员年数',
+  highTotalNum: '剩余高级会员月数',
+  downTotalNum: '剩余场景下载次数',
+  createName: '创建人',
+  createTime: '创建时间',
+  form: {
+    name: '分销商名称',
+    nickName: '联系人',
+    userName: '请输入四维看看账号',
+    userId: '账号',
+    tips: '该账号不存在',
+  },
+};

+ 2 - 1
src/locales/lang/zh-CN/code.ts

@@ -20,7 +20,8 @@ export default {
     '4018': '下载数量不足!',
     '4019': 'sn码不存在!',
     '4020': '频繁登录失败,请五分钟后再次尝试登录!',
-    '4021': '相机剩余空间不足,无法复制',
+    '4021': '分销商名称已存在',
+    '4022': '分销商账号已存在',
     '5005': '场景为空',
     '5071': '复制场景不支持重算',
     '50009': 'V3场景请先升级再重算',

+ 6 - 5
src/locales/lang/zh-CN/routes/dashboard.ts

@@ -31,7 +31,7 @@ export default {
   scenesRoom: '房间管理',
   product: '产品管理',
   firmware: '固件管理',
-  account:'用户管理',
+  account: '用户管理',
   overview: '用户概览',
   details: '订单详情',
   productData: '产品数据',
@@ -64,8 +64,9 @@ export default {
   customerDevice: '客户设备',
   customerScene: '客户场景',
   devices: '设备管理',
-  equity:'权益管理',
-  scene:'场景管理',
-  finance:'销售统计',
-  loglist:'操作日志',
+  equity: '权益管理',
+  scene: '场景管理',
+  finance: '销售统计',
+  loglist: '操作日志',
+  retailer: '分销商管理',
 };

+ 1 - 0
src/locales/lang/zh-CN/routes/device.ts

@@ -22,4 +22,5 @@ export default {
   },
   bindStatus: '绑定状态',
   statusName: '权益状态',
+  subAgentName: '分销商',
 };

+ 3 - 2
src/locales/lang/zh-CN/routes/finance.ts

@@ -11,10 +11,11 @@ export default {
   staffList: '账号列表',
   updateBtn: '修改密码',
   password: '修改密码',
+  agentName: '授权人',
   equityType: {
     0: '专业会员(年)',
     1: '高级会员(月)',
-    2: '场景下载',
+    2: '场景下载(次)',
   },
   giveType: {
     0: '经销商授权',
@@ -22,5 +23,5 @@ export default {
     2: '官网自购',
     3: '平台授权',
   },
-  totalTime:'授权总期限(年/月)',
+  totalTime: '授权总期限(年/月)',
 };

+ 28 - 0
src/locales/lang/zh-CN/routes/retailer.ts

@@ -0,0 +1,28 @@
+export default {
+  name: '分销商名称',
+  add: '新增分销商',
+  setName: '权益设置',
+  nickName: '联系人',
+  userName: '请输入四维看看账号',
+  userId: '账号',
+  tips: '该账号不存在',
+  jxs: '经销商',
+  fxs: '分销商',
+  majorAddNum: '专业会员(年):剩余可售 {value} 年,本次新增 (年数)',
+  highAddNum: '高级会员(月):剩余可售 {value} 年,本次新增 (月数)',
+  downAddNum: '场景下载(次):剩余可售 {value} 年,本次新增 (次数)',
+  ff: '分发',
+  allff: '设备批量分发',
+  majorTotalNum: '剩余专业会员年数',
+  highTotalNum: '剩余高级会员月数',
+  downTotalNum: '剩余场景下载次数',
+  createName: '创建人',
+  createTime: '创建时间',
+  form: {
+    name: '分销商名称',
+    nickName: '联系人',
+    userName: '请输入四维看看账号',
+    userId: '账号',
+    tips: '该账号不存在',
+  },
+};

+ 31 - 0
src/router/routes/modules/retailer.ts

@@ -0,0 +1,31 @@
+import type { AppRouteRecordRaw } from '/@/router/types';
+import { t } from '/@/hooks/web/useI18n';
+import { LAYOUT } from '/@/router/constant';
+import { RoleEnum } from '/@/enums/roleEnum';
+
+export const Retailer: AppRouteRecordRaw = {
+  path: '/retailer',
+  name: 'Retailer',
+  redirect: '/retailer/index',
+  component: LAYOUT,
+  meta: {
+    title: t('routes.dashboard.retailer'),
+    icon: 'fluent:building-retail-toolbox-20-regular',
+    orderNo: 4,
+    hideChildrenInMenu: true,
+    roles: [RoleEnum.SUPER],
+  },
+  children: [
+    {
+      path: 'index',
+      name: 'RetailerIndex',
+      component: () => import('/@/views/retailer/index.vue'),
+      meta: {
+        title: t('routes.dashboard.retailer'),
+        hideBreadcrumb: true,
+        // icon: 'codicon:device-camera',
+      },
+    },
+  ],
+};
+export default Retailer;

+ 24 - 16
src/store/modules/user.ts

@@ -2,7 +2,6 @@ import type { UserInfo, UserAgent } from '/#/store';
 import type { ErrorMessageMode } from '/#/axios';
 import { defineStore } from 'pinia';
 import { store } from '/@/store';
-import { RoleEnum } from '/@/enums/roleEnum';
 import { PageEnum } from '/@/enums/pageEnum';
 import { ROLES_KEY, TOKEN_KEY, USER_INFO_KEY, AGENT_KEY } from '/@/enums/cacheEnum';
 import { getAuthCache, setAuthCache } from '/@/utils/auth';
@@ -16,6 +15,7 @@ import { RouteRecordRaw } from 'vue-router';
 import { PAGE_NOT_FOUND_ROUTE } from '/@/router/routes/basic';
 import { isArray } from '/@/utils/is';
 import { h } from 'vue';
+import { RoleEnum } from '/@/enums/roleEnum';
 
 interface UserState {
   userInfo: Nullable<UserInfo>;
@@ -24,6 +24,7 @@ interface UserState {
   sessionTimeout?: boolean;
   lastUpdateTime: number;
   agent: Nullable<UserAgent>;
+  isEnv: Boolean;
 }
 
 export const useUserStore = defineStore({
@@ -40,7 +41,8 @@ export const useUserStore = defineStore({
     // Last fetch time
     lastUpdateTime: 0,
     // 经销商信息
-    agent: null
+    agent: null,
+    isEnv: window.location.hostname.includes('eur'), //true 国际服
   }),
   getters: {
     getUserInfo(): UserInfo {
@@ -61,6 +63,9 @@ export const useUserStore = defineStore({
     getLastUpdateTime(): number {
       return this.lastUpdateTime;
     },
+    getSystemEnv(): boolean {
+      return window.location.hostname.includes('eur');
+    },
   },
   actions: {
     setToken(info: string | undefined) {
@@ -71,8 +76,8 @@ export const useUserStore = defineStore({
       this.roleList = roleList;
       setAuthCache(ROLES_KEY, roleList);
     },
-    setAgent(agent: UserAgent){
-      console.log('setAgent',agent)
+    setAgent(agent: UserAgent) {
+      console.log('setAgent', agent);
       this.agent = agent;
       setAuthCache(AGENT_KEY, agent);
     },
@@ -103,14 +108,14 @@ export const useUserStore = defineStore({
         const { goHome = true, mode, ...loginParams } = params;
         const data = await loginApi(loginParams, mode);
         const { token, user, agent } = data;
-        this.setAgent(agent)
+        this.setAgent(agent);
         this.setUserInfo({
           ...user,
-          userId:user.id,
+          userId: user.id,
           realName: user.nickName,
-          username:user.userName,
-          avatar:user.head||'https://q1.qlogo.cn/g?b=qq&nk=339449197&s=640',
-          roles:[data.roleId],
+          username: user.userName,
+          avatar: user.head || 'https://q1.qlogo.cn/g?b=qq&nk=339449197&s=640',
+          roles: agent && agent.parentId ? [RoleEnum.TEST] : [RoleEnum.SUPER, RoleEnum.TEST],
         });
         // save token
         this.setToken(token);
@@ -144,16 +149,19 @@ export const useUserStore = defineStore({
       if (!this.getToken) return null;
       const userInfo = await getUserInfo();
       const { roleId, agent = null } = userInfo;
-      this.setAgent(agent)
-      if (isArray(roleId)) { 
+      this.setAgent(agent);
+      if (isArray(roleId)) {
         const roleList = roleId.map((item) => item.value) as RoleEnum[];
         this.setRoleList(roleList);
-      }else if(roleId) {
+      } else if (roleId) {
         userInfo.roles = [roleId];
-        this.setRoleList([]);
-      }else{
-        userInfo.roles = [];
-        this.setRoleList([]);
+        this.setRoleList([RoleEnum.SUPER, RoleEnum.TEST]);
+      } else if (agent && agent.parentId) {
+        userInfo.roles = [RoleEnum.TEST];
+        this.setRoleList([RoleEnum.SUPER, RoleEnum.TEST]);
+      } else {
+        userInfo.roles = [RoleEnum.SUPER];
+        this.setRoleList([RoleEnum.SUPER, RoleEnum.TEST]);
       }
       this.setUserInfo(userInfo);
       return userInfo;

+ 183 - 0
src/views/device/distributeModal.vue

@@ -0,0 +1,183 @@
+<template>
+  <BasicModal
+    v-bind="$attrs"
+    @register="register"
+    title="设备入库"
+    @visible-change="handleVisibleChange"
+    @cancel="resetFields"
+    @ok="handleSubmit"
+    :min-height="150"
+  >
+    <div class="pt-2px pr-3px">
+      <BasicForm @register="registerForm" :model="model">
+        <template #text="{ model, field }">
+          {{ model[field] }}
+        </template>
+      </BasicForm>
+      <a v-if="!model.id" @click="getTemplate" style="padding: 20px 0 0 80px">下载分销商设备清单模板</a>
+    </div>
+  </BasicModal>
+</template>
+<script lang="ts">
+  import { defineComponent, ref, nextTick, onMounted, reactive } from 'vue';
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import { BasicForm, FormSchema, useForm } from '/@/components/Form/index';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import { cameraIn } from '/@/api/device';
+  import { getSubAgent, downTemplate, giveCamera, giveCameraBatch } from '/@/api/retailer';
+  import { companyUploadExcel } from '/@/api/customer';
+  import { downloadByData } from '/@/utils/file/download'
+  import { uploadApi } from '/@/api/product/index';
+  import { useI18n } from '/@/hooks/web/useI18n';
+
+  const { t } = useI18n();
+  export default defineComponent({
+    components: { BasicModal, BasicForm },
+    props: {
+      userData: { type: Object },
+    },
+    emits: ['reload'],
+    setup(_, { emit }) {
+      const modelRef = ref({});
+      const fileFlow = reactive({
+        file: null,
+        title: '设备分发',
+      });
+      const { createMessage } = useMessage();
+      const schemas: FormSchema[] = [
+        {
+          field: 'id',
+          component: 'Input',
+          label: t('routes.product.types'),
+          show: false,
+          colProps: {
+            span: 24,
+          },
+        },
+        {
+          field: 'subAgentId',
+          component: 'ApiSelect',
+          label: '分销商名称',
+          required: true,
+          colProps: {
+            span: 18,
+          },
+          componentProps: {
+            maxLength: 50,
+            api: getSubAgent,
+            // numberToString: true,
+            labelField: 'name',
+            valueField: 'id',
+            immediate: true,
+          },
+          colProps: {
+            xl: 18,
+            xxl: 18,
+          },
+        },
+        {
+          field: 'file',
+          component: 'Upload',
+          label: '设备清单',
+          required: true,
+          rules: [{ required: true, message: t('common.uploadMessge') }],
+          // helpMessage: t('routes.corporation.uploadHelp'),
+          itemProps: {
+            validateTrigger: 'onBlur',
+          },
+          componentProps: {
+            api: uploadApi,
+            maxNumber: 1,
+            maxSize: 1000,
+            fileFlow:true,
+            accept: ['xlsx', 'xls'],
+            afterFetch: function (data) {
+              // Reflect.set(data, 'url', data.file);
+              fileFlow.file = data.file
+              return data;
+            },
+          },
+
+          colProps: {
+            span: 22,
+          },
+        },
+      ];
+      const [registerForm, { validate, resetFields, setFieldsValue, updateSchema }] = useForm({
+        labelWidth: 120,
+        schemas,
+        showActionButtonGroup: false,
+        actionColOptions: {
+          span: 24,
+        },
+      });
+      onMounted(() => {});
+      let addListFunc = () => {};
+      const [register, { closeModal }] = useModalInner((data) => {
+        // console.log(data);
+        data && onDataReceive(data);
+      });
+
+      function onDataReceive(data) {
+        modelRef.value = data;
+        resetFields();
+        updateSchema({
+          field: 'file',
+          show: data.id ? false : true,
+        })
+        setFieldsValue({
+          id: data.id,
+          subAgentId: data.subAgentId,
+        });
+      }
+      async function getTemplate() {
+        try {
+          const res:BlobPart = await downTemplate({ type: 0 });
+          console.log('downTemplate',res)
+          downloadByData(res.data,'分销商设备清单模板.xlsx')
+        } catch (error) {
+          console.log('not passing', error);
+        }
+      }
+
+      function handleVisibleChange() {}
+
+      const handleSubmit = async () => {
+        try {
+          const params = await validate();
+          console.log('params', params);
+          if (params.id) {
+            await giveCamera(params);
+          } else {
+            const apiData = {
+              file: fileFlow.file,
+              data: {
+                subAgentId: params.subAgentId,
+              }
+            };
+            await giveCameraBatch(apiData);
+          }
+          closeModal();
+          resetFields();
+          createMessage.success(t('common.optSuccess'));
+          emit('reload');
+        } catch (error) {
+          console.log('not passing', error);
+        }
+      };
+      return {
+        register,
+        schemas,
+        registerForm,
+        model: modelRef,
+        fileFlow,
+        handleVisibleChange,
+        handleSubmit,
+        addListFunc,
+        resetFields,
+        getTemplate,
+        t,
+      };
+    },
+  });
+</script>

+ 346 - 270
src/views/device/index.vue

@@ -1,310 +1,386 @@
 <template>
   <PageWrapper contentBackground>
     <div class="desc-wrap-BasicTable">
-      <BasicTable @register="registerTable"> </BasicTable>
+      <BasicTable @register="registerTable">
+        <template #toolbar>
+          <a-button type="primary" @click="handleAdd"> {{t('routes.retailer.allff')}}</a-button>
+        </template>
+        <template #action="{ record }">
+          <TableAction
+            stopButtonPropagation
+            :actions="[
+              {
+                label: t('routes.retailer.ff'),
+                //icon: 'la:file-invoice-dollar',
+                onClick: headleDetails.bind(null, record),
+              },
+            ]"
+          />
+        </template>
+      </BasicTable>
     </div>
+    <distributeModal @reload="reload" @register="registerAddModal" />
   </PageWrapper>
 </template>
 <script lang="ts">
-import { defineComponent, h, onMounted, computed } from 'vue';
-import {
-  BasicTable,
-  useTable,
-  TableAction,
-  BasicColumn,
-  TableImg,
-  FormProps,
-} from '/@/components/Table';
-import { PageWrapper } from '/@/components/Page';
-import { Descriptions } from 'ant-design-vue';
-import { useI18n } from '/@/hooks/web/useI18n';
-import { useMessage } from '/@/hooks/web/useMessage';
-import { cameraList } from '/@/api/customer';
-import { cameraDelete } from '/@/api/device';
-import { useRouter } from 'vue-router';
-import { UnbindCameraApi } from '/@/api/account';
-import { usePermissionStore } from '/@/store/modules/permission';
-import { useLocaleStore } from '/@/store/modules/locale';
-import { dincrementList } from '/@/api/equity';
-export default defineComponent({
-  components: {
+  import { defineComponent, h, onMounted, computed } from 'vue';
+  import {
     BasicTable,
+    useTable,
     TableAction,
-    PageWrapper,
+    BasicColumn,
     TableImg,
-    [Descriptions.name]: Descriptions,
-    [Descriptions.Item.name]: Descriptions.Item,
-  },
-  setup() {
-    const { t } = useI18n();
-    const { createMessage, createConfirm } = useMessage();
-    const permissionStore = usePermissionStore();
-    const { getCheckPerm } = permissionStore;
-    const router = useRouter();
-    const localeStore = useLocaleStore();
-    const isEn = computed(() => localeStore.getLocale === 'en');
-    const companyId: Number = router.currentRoute.value.params.id - 0;
-    onMounted(() => {
-      // console.log(router.currentRoute.value.params.id);
-    });
-    const columns: BasicColumn[] = [
-      {
-        title: t('routes.device.snCode'),
-        dataIndex: 'snCode',
-        width: 180,
-      },
-      {
-        title: t('routes.device.wifiName'),
-        dataIndex: 'wifiName',
-        width: 150,
-      },
-      {
-        title: t('routes.device.deviceType'),
-        dataIndex: 'type',
-        ellipsis: false,
-        width: 100,
-        customRender: ({ record }) => {
-          let typeObj = {
-            '0': t('routes.device.type.0'),
-            '1': t('routes.device.type.1'),
-            '2': t('routes.device.type.2'),
-            '9': t('routes.device.type.9'),
-            '10': t('routes.device.type.10'),
-            '11': t('routes.device.type.11'),
-          };
-          return typeObj[record.type];
+    FormProps,
+  } from '/@/components/Table';
+  import { PageWrapper } from '/@/components/Page';
+  import { Descriptions } from 'ant-design-vue';
+  import { useI18n } from '/@/hooks/web/useI18n';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import { cameraList } from '/@/api/customer';
+  import { cameraDelete } from '/@/api/device';
+  import { useModal } from '/@/components/Modal';
+  import { getSubAgent } from '/@/api/retailer';
+  import { useRouter } from 'vue-router';
+  import { UnbindCameraApi } from '/@/api/account';
+  import { usePermissionStore } from '/@/store/modules/permission';
+  import { useLocaleStore } from '/@/store/modules/locale';
+  import { dincrementList } from '/@/api/equity';
+  import { useUserStore } from '/@/store/modules/user';
+  import distributeModal from './distributeModal.vue';
+  export default defineComponent({
+    components: {
+      BasicTable,
+      TableAction,
+      PageWrapper,
+      TableImg,
+      distributeModal,
+      [Descriptions.name]: Descriptions,
+      [Descriptions.Item.name]: Descriptions.Item,
+    },
+    setup() {
+      const { t } = useI18n();
+      const userStore = useUserStore();
+      const getUserInfo = userStore.getUserInfo;
+      console.log('getUserInfo', getUserInfo);
+      const { createMessage, createConfirm } = useMessage();
+      const permissionStore = usePermissionStore();
+      const { getCheckPerm } = permissionStore;
+      const router = useRouter();
+      const localeStore = useLocaleStore();
+      const isEn = computed(() => localeStore.getLocale === 'en');
+      const companyId: Number = router.currentRoute.value.params.id - 0;
+      const [registerAddModal, { openModal }] = useModal();
+      onMounted(() => {
+        // console.log(router.currentRoute.value.params.id);
+      });
+      const columns: BasicColumn[] = [
+        {
+          title: t('routes.device.snCode'),
+          dataIndex: 'snCode',
+          width: 180,
+        },
+        {
+          title: t('routes.device.wifiName'),
+          dataIndex: 'wifiName',
+          width: 150,
+        },
+        {
+          title: t('routes.device.deviceType'),
+          dataIndex: 'type',
+          ellipsis: false,
+          width: 100,
+          customRender: ({ record }) => {
+            let typeObj = {
+              '0': t('routes.device.type.0'),
+              '1': t('routes.device.type.1'),
+              '2': t('routes.device.type.2'),
+              '9': t('routes.device.type.9'),
+              '10': t('routes.device.type.10'),
+              '11': t('routes.device.type.11'),
+            };
+            return typeObj[record.type];
+          },
         },
-      },
 
-      {
-        title: t('routes.device.activatedTime'),
-        dataIndex: 'activatedTime',
-        width: 180,
-      },
-      {
-        title: t('routes.equity.Type'),
-        dataIndex: 'validTimeType',
-        width: 180,
-        customRender({ record }) {
-          return record.validTimeType==0 ?t('routes.equity.equityType.0'):record.validTimeType==1? t('routes.equity.equityType.3') : t('routes.device.NoBind');
+        {
+          title: t('routes.device.activatedTime'),
+          dataIndex: 'activatedTime',
+          width: 180,
         },
-      },
-      {
-        title: t('routes.device.statusName'),
-        dataIndex: 'incrementStatus',
-        customRender({ record }) {
-          if(record.incrementStatus == -1){
-            return '-'
-          }else{
-            return record.incrementStatus==0? t('routes.device.status.0') : t('routes.device.status.1');
+        {
+          title: t('routes.retailer.fxs'),
+          dataIndex: 'subAgentName',
+          width: 120,
+          customRender({ record }) {
+            return record.subAgentName || '-';
           }
         },
-        width: 180,
-      },
-      {
-        title: t('routes.device.userName'),
-        dataIndex: 'userName',
-        width: 180,
-        customRender({ record }) {
-          return record.userName ? record.userName : '-';
-        },
-      },
-    ];
-    const searchForm: Partial<FormProps> = {
-      labelWidth: 120,
-      autoAdvancedLine:1,
-      actionColOptions: {
-        span: 24,
-      },
-      schemas: [
         {
-          field: 'snCode',
-          component: 'Input',
-          label: t('routes.device.snCode'),
-          colProps: {
-            xl: 8,
-            xxl: 8,
+          title: t('routes.equity.Type'),
+          dataIndex: 'validTimeType',
+          width: 180,
+          customRender({ record }) {
+            return record.validTimeType == 0
+              ? t('routes.equity.equityType.0')
+              : record.validTimeType == 1
+              ? t('routes.equity.equityType.3')
+              : t('routes.device.NoBind');
           },
         },
         {
-          field: 'type',
-          component: 'Select',
-          label: t('routes.device.deviceType'),
-          colProps: {
-            xl: 8,
-            xxl: 8,
-          },
-          componentProps: {
-            options: [
-              {
-                //   label: t('routes.device.type.0'),
-                //   value: 0,
-                //   key: '0',
-                // },{
-                label: t('routes.device.type.1'),
-                value: 1,
-                key: '1',
-              },
-              {
-                label: t('routes.device.type.2'),
-                value: 9,
-                key: '9',
-              },
-              {
-                label: t('routes.device.type.3'),
-                value: 10,
-                key: '10',
-              },
-              {
-                label: t('routes.device.type.11'),
-                value: 11,
-                key: '11',
-              },
-            ],
+          title: t('routes.device.statusName'),
+          dataIndex: 'incrementStatus',
+          customRender({ record }) {
+            if (record.incrementStatus == -1) {
+              return '-';
+            } else {
+              return record.incrementStatus == 0
+                ? t('routes.device.status.0')
+                : t('routes.device.status.1');
+            }
           },
+          width: 180,
         },
         {
-          field: 'userName',
-          component: 'Input',
-          label: t('routes.device.userName'),
-          colProps: {
-            xl: 8,
-            xxl: 8,
+          title: t('routes.device.userName'),
+          dataIndex: 'userName',
+          width: 180,
+          customRender({ record }) {
+            return record.userName ? record.userName : '-';
           },
         },
-        {
-          field: 'incrementTypeId',
-          component: 'ApiSelect',
-          label: t('routes.equity.Type'),
-          componentProps: {
-            maxLength: 50,
-            api: async function () {
-              const list = await dincrementList();
-              return list.map((ele) => {
-                return { name: t(`routes.finance.equityType.${ele.validTimeType}`), value: ele.id };
-              });
+      ];
+      const searchForm: Partial<FormProps> = {
+        labelWidth: 120,
+        autoAdvancedLine: 1,
+        actionColOptions: {
+          span: 24,
+        },
+        schemas: [
+          {
+            field: 'snCode',
+            component: 'Input',
+            label: t('routes.device.snCode'),
+            colProps: {
+              xl: 8,
+              xxl: 8,
             },
-            numberToString: true,
-            labelField: 'name',
-            valueField: 'value',
-            immediate: true,
           },
-          colProps: {
-            xl: 8,
-            xxl: 8,
+          {
+            field: 'type',
+            component: 'Select',
+            label: t('routes.device.deviceType'),
+            colProps: {
+              xl: 8,
+              xxl: 8,
+            },
+            componentProps: {
+              options: [
+                {
+                  //   label: t('routes.device.type.0'),
+                  //   value: 0,
+                  //   key: '0',
+                  // },{
+                  label: t('routes.device.type.1'),
+                  value: 1,
+                  key: '1',
+                },
+                {
+                  label: t('routes.device.type.2'),
+                  value: 9,
+                  key: '9',
+                },
+                {
+                  label: t('routes.device.type.3'),
+                  value: 10,
+                  key: '10',
+                },
+                {
+                  label: t('routes.device.type.11'),
+                  value: 11,
+                  key: '11',
+                },
+              ],
+            },
           },
-        },
-        {
-          field: 'incrementStatus',
-          component: 'Select',
-          label: t('routes.device.statusName'),
-          colProps: {
-            xl: 8,
-            xxl: 8,
+          {
+            field: 'userName',
+            component: 'Input',
+            label: t('routes.device.userName'),
+            colProps: {
+              xl: 8,
+              xxl: 8,
+            },
           },
-          componentProps: {
-            options: [
-              {
-                label: t('routes.device.status.0'),
-                value: '0',
-                key: '0',
-              },
-              {
-                label: t('routes.device.status.1'),
-                value: '1',
-                key: '1',
+          {
+            field: 'incrementTypeId',
+            component: 'ApiSelect',
+            label: t('routes.equity.Type'),
+            componentProps: {
+              maxLength: 50,
+              api: async function () {
+                const list = await dincrementList();
+                return list.map((ele) => {
+                  return {
+                    name: t(`routes.finance.equityType.${ele.validTimeType}`),
+                    value: ele.id,
+                  };
+                });
               },
-            ],
+              numberToString: true,
+              labelField: 'name',
+              valueField: 'value',
+              immediate: true,
+            },
+            colProps: {
+              xl: 8,
+              xxl: 8,
+            },
           },
-        },
-        {
-          field: 'bindStatus',
-          component: 'Select',
-          label: t('routes.device.bindStatus'),
-          colProps: {
-            xl: 8,
-            xxl: 8,
+          {
+            field: 'incrementStatus',
+            component: 'Select',
+            label: t('routes.device.statusName'),
+            colProps: {
+              xl: 8,
+              xxl: 8,
+            },
+            componentProps: {
+              options: [
+                {
+                  label: t('routes.device.status.0'),
+                  value: '0',
+                  key: '0',
+                },
+                {
+                  label: t('routes.device.status.1'),
+                  value: '1',
+                  key: '1',
+                },
+              ],
+            },
           },
-          componentProps: {
-            options: [
-              {
-                label: t('routes.device.status.2'),
-                value: '1',
-                key: '1',
-              },
-              {
-                label: t('routes.device.status.3'),
-                value: '0',
-                key: '0',
-              },
-            ],
+          {
+            field: 'bindStatus',
+            component: 'Select',
+            label: t('routes.device.bindStatus'),
+            colProps: {
+              xl: 8,
+              xxl: 8,
+            },
+            componentProps: {
+              options: [
+                {
+                  label: t('routes.device.status.2'),
+                  value: '1',
+                  key: '1',
+                },
+                {
+                  label: t('routes.device.status.3'),
+                  value: '0',
+                  key: '0',
+                },
+              ],
+            },
           },
+          {
+            field: 'subAgentId',
+            component: 'ApiSelect',
+            label: t('routes.device.subAgentName'),
+            componentProps: {
+              maxLength: 50,
+              api: getSubAgent,
+              numberToString: true,
+              labelField: 'name',
+              valueField: 'id',
+              immediate: true,
+            },
+            colProps: {
+              xl: 8,
+              xxl: 8,
+            },
+          },
+        ],
+      };
+      const [registerTable, { reload }] = useTable({
+        api: cameraList,
+        columns: columns,
+        searchInfo: { companyId },
+        useSearchForm: true,
+        formConfig: searchForm,
+        showTableSetting: true,
+        showIndexColumn: false,
+        rowKey: 'id',
+        beforeFetch: (T) => {
+          if (T.ctivated) {
+            T.activatedStartTime = T.ctivated[0];
+            T.activatedEndTime = T.ctivated[1];
+          }
+          return T;
         },
-      ],
-    };
-    const [registerTable, { reload }] = useTable({
-      api: cameraList,
-      columns: columns,
-      searchInfo: { companyId },
-      useSearchForm: true,
-      formConfig: searchForm,
-      showTableSetting: true,
-      showIndexColumn: false,
-      rowKey: 'id',
-      beforeFetch: (T) => {
-        if (T.ctivated) {
-          T.activatedStartTime = T.ctivated[0];
-          T.activatedEndTime = T.ctivated[1];
-        }
-        return T;
-      },
-      fetchSetting: {
-        pageField: 'pageNum',
-        sizeField: 'pageSize',
-        listField: 'list',
-        totalField: 'total',
-      },
-      canResize: false,
-    });
-    async function handleUnbind(record: Recordable) {
-      createConfirm({
-        iconType: 'warning',
-        title: () => h('span', t('common.optSuccess')),
-        content: '解绑后用户将看不到该相机拍摄的场景。<br/>确定解绑吗?',
-        onOk: async () => {
-          await UnbindCameraApi({ cameraId: record.id });
-          createMessage.success(t('common.optSuccess'));
-          reload();
+        fetchSetting: {
+          pageField: 'pageNum',
+          sizeField: 'pageSize',
+          listField: 'list',
+          totalField: 'total',
         },
-      });
-    }
-    async function handleDelete(record: Recordable) {
-      createConfirm({
-        iconType: 'warning',
-        title: () => h('span', t('common.optSuccess')),
-        content: '删除设备后需要重新入库<br/>确定删除吗?',
-        onOk: async () => {
-          await cameraDelete({ id: record.id });
-          createMessage.success(t('common.optSuccess'));
-          reload();
+        actionColumn: {
+          width: 50,
+          title: t('common.operating'),
+          dataIndex: 'action',
+          slots: { customRender: 'action' },
         },
+        canResize: false,
       });
-    }
-    return {
-      registerTable,
-      handleUnbind,
-      reload,
-      handleDelete,
-      getCheckPerm,
-    };
-  },
-});
+      async function handleUnbind(record: Recordable) {
+        createConfirm({
+          iconType: 'warning',
+          title: () => h('span', t('common.optSuccess')),
+          content: '解绑后用户将看不到该相机拍摄的场景。<br/>确定解绑吗?',
+          onOk: async () => {
+            await UnbindCameraApi({ cameraId: record.id });
+            createMessage.success(t('common.optSuccess'));
+            reload();
+          },
+        });
+      }
+      async function handleDelete(record: Recordable) {
+        createConfirm({
+          iconType: 'warning',
+          title: () => h('span', t('common.optSuccess')),
+          content: '删除设备后需要重新入库<br/>确定删除吗?',
+          onOk: async () => {
+            await cameraDelete({ id: record.id });
+            createMessage.success(t('common.optSuccess'));
+            reload();
+          },
+        });
+      }
+      function handleAdd() {
+        openModal(true, {});
+      }
+      function headleDetails(record: Recordable) {
+        openModal(true, record);
+      }
+      return {
+        registerTable,
+        handleUnbind,
+        reload,
+        handleDelete,
+        getCheckPerm,
+        handleAdd,
+        headleDetails,
+        registerAddModal,
+        t,
+      };
+    },
+  });
 </script>
 <style lang="less" scoped>
-.desc-wrap-BasicTable {
-  background-color: #f0f2f5;
-  .vben-basic-table-form-container {
-    padding: 0;
+  .desc-wrap-BasicTable {
+    background-color: #f0f2f5;
+    .vben-basic-table-form-container {
+      padding: 0;
+    }
   }
-}
 </style>

+ 44 - 0
src/views/finance/GrowCard.vue

@@ -0,0 +1,44 @@
+<template>
+  <div class="md:flex">
+    <template v-for="(item, index) in list" :key="item.title">
+      <Card
+        size="small"
+        :loading="loading"
+        :title="item.name"
+        class="md:w-1/6 w-full !md:mt-0 !mt-6"
+        :class="[index + 1 < 6 && '!md:mr-6']"
+        :canExpan="false"
+      >
+        <template #extra>
+          <Tag :color="item.color">{{ item.action }}</Tag>
+        </template>
+
+        <div class="py-4 flex justify-between">
+          <CountTo :prefix="item.unit" :startVal="0" :endVal="item.value" class="text-2xl" />
+          <!-- <Icon :icon="item.icon" :size="40" /> -->
+        </div>
+
+        <div class="flex justify-between">
+          <span>{{ item.title }}</span>
+          <!-- <CountTo :prefix="item.unit" :startVal="0" :endVal="item.value" /> -->
+        </div>
+      </Card>
+    </template>
+  </div>
+</template>
+<script lang="ts" setup>
+  import { CountTo } from '/@/components/CountTo/index';
+  import { Icon } from '/@/components/Icon';
+  import { Tag, Card } from 'ant-design-vue';
+  import { growCardList, GrowCardItem } from '../data';
+
+  defineProps({
+    loading: {
+      type: Boolean,
+    },
+    list: {
+      type: Array as PropType<Array<GrowCardItem>>,
+      default: [],
+    },
+  });
+</script>

+ 31 - 7
src/views/finance/data.tsx

@@ -11,6 +11,7 @@ import dayjs from 'dayjs';
 import { useLocaleStore } from '/@/store/modules/locale';
 const localeStore = useLocaleStore();
 const isEn = computed(() => localeStore.getLocale === 'en');
+import { getSubAgent } from '/@/api/retailer';
 
 export const columns: BasicColumn[] = [
   {
@@ -52,6 +53,12 @@ export const columns: BasicColumn[] = [
     width: 150,
   },
   {
+    title: t('routes.finance.agentName'),
+    dataIndex: 'agentName',
+    // slots: { customRender: 'orderType' },
+    width: 150,
+  },
+  {
     title: t('routes.equity.userCount'),
     dataIndex: 'count',
     // slots: { customRender: 'orderStatus' },
@@ -63,7 +70,7 @@ export const columns: BasicColumn[] = [
     // slots: { customRender: 'orderStatus' },
     width: 120,
     customRender: ({ record }) => {
-      return record.type == 2?'-':record.totalTime;
+      return record.type == 2 ? '-' : record.totalTime;
     },
   },
   {
@@ -80,15 +87,32 @@ export const searchForm: Partial<FormProps> = {
   labelWidth: isEn.value ? 140 : 100,
   schemas: [
     {
+      field: 'subAgentId',
+      component: 'ApiSelect',
+      label: t('routes.finance.agentName'),
+      componentProps: {
+        maxLength: 50,
+        api: getSubAgent,
+        numberToString: true,
+        labelField: 'name',
+        valueField: 'id',
+        immediate: true,
+      },
+      colProps: {
+        xl: 8,
+        xxl: 8,
+      },
+    },
+    {
       field: 'type',
       label: t('routes.equity.Type'),
       component: 'ApiSelect',
-      labelWidth:70,
+      labelWidth: 70,
       componentProps: {
         maxLength: 50,
         api: async function () {
           const list = await dincrementList();
-          let optionList = [];
+          const optionList = [];
           list.map((ele) => {
             optionList.push({
               name: t(`routes.finance.equityType.${ele.validTimeType}`),
@@ -110,8 +134,8 @@ export const searchForm: Partial<FormProps> = {
         immediate: true,
       },
       colProps: {
-        xl: 6,
-        xxl: 6,
+        xl: 8,
+        xxl: 8,
       },
     },
     {
@@ -145,8 +169,8 @@ export const searchForm: Partial<FormProps> = {
         ],
       },
       colProps: {
-        xl: 7,
-        xxl: 6,
+        xl: 8,
+        xxl: 8,
       },
     },
     {

+ 213 - 173
src/views/finance/index.vue

@@ -3,7 +3,9 @@
     <GrowCard :loading="loading" class="enter-y" :list="growCardList" />
     <BasicTable @register="registerTable">
       <template #toolbar>
-        <a-button type="primary" @click="exportExcel" > {{t('routes.equity.exportExcel')}}</a-button>
+        <a-button type="primary" @click="exportExcel">
+          {{ t('routes.equity.exportExcel') }}</a-button
+        >
       </template>
     </BasicTable>
     <AddModal @update="reload" @register="registerAddModal" />
@@ -11,180 +13,218 @@
   </div>
 </template>
 <script lang="ts">
-import { defineComponent, h, reactive, ref, onMounted, computed } from 'vue';
-import GrowCard from '../dashboard/analysis/components/GrowCard.vue';
-import { BasicTable, useTable, TableAction, TableImg } from '/@/components/Table';
-import { PageWrapper } from '/@/components/Page';
-import { useModal } from '/@/components/Modal';
-import { Descriptions } from 'ant-design-vue';
-import { useI18n } from '/@/hooks/web/useI18n';
-import { useMessage } from '/@/hooks/web/useMessage';
-import { logData, logList, DownExport } from '/@/api/finance';
-import { searchForm, columns } from './data';
-import AddModal from './InvoiceModal.vue';
-import EditModal from './EditModal.vue';
-import { usePermissionStore } from '/@/store/modules/permission';
-import { useLocaleStore } from '/@/store/modules/locale';
+  import { defineComponent, h, reactive, ref, onMounted, computed } from 'vue';
+  import GrowCard from './GrowCard.vue';
+  import { BasicTable, useTable, TableAction, TableImg } from '/@/components/Table';
+  import { PageWrapper } from '/@/components/Page';
+  import { useModal } from '/@/components/Modal';
+  import { Descriptions } from 'ant-design-vue';
+  import { useI18n } from '/@/hooks/web/useI18n';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import { logData, logList, DownExport } from '/@/api/finance';
+  import { searchForm, columns } from './data';
+  import AddModal from './InvoiceModal.vue';
+  import EditModal from './EditModal.vue';
+  import { usePermissionStore } from '/@/store/modules/permission';
+  import { useLocaleStore } from '/@/store/modules/locale';
 
-interface apiDataParam {
-  orderSn?: string;
-  payTimeStart?: string;
-  payTimeEnd?: string;
-  invoiceTimeStart?: string;
-  invoiceTimeEnd?: string;
-  orderBy?: string;
-  sortBy?: string;
-}
-interface GrowCardItem {
-  icon: string;
-  title: string;
-  value: number;
-  unit: string;
-  color: string;
-  action: string;
-}
-export default defineComponent({
-  components: {
-    AddModal,
-    GrowCard,
-    EditModal,
-    BasicTable,
-    TableAction,
-    PageWrapper,
-    TableImg,
-    [Descriptions.name]: Descriptions,
-    [Descriptions.Item.name]: Descriptions.Item,
-  },
-  setup() {
-    const { t } = useI18n();
-    const loading = ref(true);
-    const growCardList = ref<GrowCardItem[]>([]);
-    const localeStore = useLocaleStore();
-    const isEn = computed(() => localeStore.getLocale === 'en');
-    onMounted(async () => {
-      const {lastDownNum = 0,lastHighNum = 0,lastMajorNum = 0} = await logData({})
-      loading.value = false
-      growCardList.value = [{
-        title: t('routes.equity.CardMajorNum'),
-        // icon: 'fa6-solid:users-gear',
-        icon: 'visit-count|svg',
-        value: lastMajorNum || 0,
-        unit: t('routes.equity.unit.-1'),
-        color: 'blue',
-        action: t('routes.equity.unit.-2'),
-      },
-      {
-        title:  t('routes.equity.CardHighNum'),
-        icon: 'akar-icons:person-add',
-        value: lastHighNum || 0,
-        unit: t('routes.equity.unit.-2'),
-        color: 'blue',
-        action: t('routes.equity.unit.-2'),
-      },
-      {
-        title: t('routes.equity.CardDownNum'),
-        icon: 'carbon:user-role',
-        value: lastDownNum || 0,
-        unit: t('routes.equity.unit.2'),
-        color: 'blue',
-        action: t('routes.equity.unit.-2'),
-      },]
-    });
-    const permissionStore = usePermissionStore();
-    const { getCheckPerm } = permissionStore;
-    const apiData = reactive<apiDataParam>({
-      orderSn: '',
-      payTimeStart: '',
-      payTimeEnd: '',
-      invoiceTimeStart: '',
-      invoiceTimeEnd: '',
-      orderBy: '',
-      sortBy: '',
-    });
-    const { createMessage, createConfirm } = useMessage();
-    const [registerAddModal, { openModal: openAddModal }] = useModal();
-    const [registerEditModal, { openModal: openEditModal }] = useModal();
-    const [registerTable, { reload }] = useTable({
-      api: logList,
-      title: t('routes.equity.listTitle'),
-      titleHelpMessage: [t('routes.equity.titleHelpMessage.1'), t('routes.equity.titleHelpMessage.2'),t('routes.equity.titleHelpMessage.3'),],
-      columns: columns,
-      useSearchForm: true,
-      formConfig: searchForm,
-      showTableSetting: true,
-      showIndexColumn: false,
-      rowKey: 'id',
-      beforeFetch: (params) => {
-        let searchData = {
-          orderSn: params.orderSn,
-          payTimeStart: params.payTime && params.payTime[0],
-          payTimeEnd: params.payTime && params.payTime[1],
-          invoiceTimeStart: params.invoiceTime && params.invoiceTime[0],
-          invoiceTimeEnd: params.invoiceTime && params.invoiceTime[1],
-        };
-        apiData.orderSn = searchData.orderSn;
-        apiData.payTimeStart = searchData.payTimeStart;
-        apiData.payTimeEnd = searchData.payTimeEnd;
-        apiData.invoiceTimeStart = searchData.invoiceTimeStart;
-        apiData.invoiceTimeEnd = searchData.invoiceTimeEnd;
-        return {
-          ...params,
-          ...searchData,
-        };
-      },
-      fetchSetting: {
-        pageField: 'pageNum',
-        sizeField: 'pageSize',
-        listField: 'list',
-        totalField: 'total',
-      },
-      canResize: false,
-    });
-    function handleDelete(record: Recordable) {
-      console.log('点击了删除', record);
-    }
-    function handleInvoice(record: Recordable) {
-      openAddModal(true, {
-        ...record,
+  interface apiDataParam {
+    orderSn?: string;
+    payTimeStart?: string;
+    payTimeEnd?: string;
+    invoiceTimeStart?: string;
+    invoiceTimeEnd?: string;
+    orderBy?: string;
+    sortBy?: string;
+  }
+  interface GrowCardItem {
+    icon: string;
+    title: string;
+    value: number;
+    unit: string;
+    color: string;
+    action: string;
+    name: string;
+  }
+  export default defineComponent({
+    components: {
+      AddModal,
+      GrowCard,
+      EditModal,
+      BasicTable,
+      TableAction,
+      PageWrapper,
+      TableImg,
+      [Descriptions.name]: Descriptions,
+      [Descriptions.Item.name]: Descriptions.Item,
+    },
+    setup() {
+      const { t } = useI18n();
+      const loading = ref(true);
+      const growCardList = ref<GrowCardItem[]>([]);
+      const localeStore = useLocaleStore();
+      const isEn = computed(() => localeStore.getLocale === 'en');
+      onMounted(async () => {
+        const { agentData, subAgentData } = await logData({});
+        loading.value = false;
+        growCardList.value = [
+          {
+            title: t('routes.equity.CardMajorNum'),
+            name: t('routes.finance.equityType.0'),
+            // icon: 'fa6-solid:users-gear',
+            icon: 'visit-count|svg',
+            value: agentData.lastMajorNum || 0,
+            unit: t('routes.equity.unit.-1'),
+            color: 'blue',
+            action: t('routes.retailer.jxs'),
+          },
+          {
+            title: t('routes.equity.CardHighNum'),
+            name: t('routes.finance.equityType.1'),
+            icon: 'akar-icons:person-add',
+            value: agentData.lastHighNum || 0,
+            unit: t('routes.equity.unit.-2'),
+            color: 'blue',
+            action: t('routes.retailer.jxs'),
+          },
+          {
+            title: t('routes.equity.CardDownNum'),
+            name: t('routes.finance.equityType.2'),
+            icon: 'carbon:user-role',
+            value: agentData.lastDownNum || 0,
+            unit: t('routes.equity.unit.2'),
+            color: 'blue',
+            action: t('routes.retailer.jxs'),
+          },
+          {
+            title: t('routes.equity.CardMajorNum'),
+            name: t('routes.finance.equityType.0'),
+            // icon: 'fa6-solid:users-gear',
+            icon: 'visit-count|svg',
+            value: subAgentData.lastMajorNum || 0,
+            unit: t('routes.equity.unit.-1'),
+            color: 'orange',
+            action: t('routes.retailer.fxs'),
+          },
+          {
+            title: t('routes.equity.CardHighNum'),
+            name: t('routes.finance.equityType.1'),
+            icon: 'akar-icons:person-add',
+            value: subAgentData.lastHighNum || 0,
+            unit: t('routes.equity.unit.-2'),
+            color: 'orange',
+            action: t('routes.retailer.fxs'),
+          },
+          {
+            title: t('routes.equity.CardDownNum'),
+            name: t('routes.finance.equityType.2'),
+            icon: 'carbon:user-role',
+            value: subAgentData.lastDownNum || 0,
+            unit: t('routes.equity.unit.2'),
+            color: 'orange',
+            action: t('routes.retailer.fxs'),
+          },
+        ];
       });
-    }
-    function handleEdit(record: Recordable) {
-      console.log('record', record);
-    }
-    function headleDetails(record: Recordable) {
-      console.log('record', record);
-      openEditModal(true, {
-        ...record,
+      const permissionStore = usePermissionStore();
+      const { getCheckPerm } = permissionStore;
+      const apiData = reactive<apiDataParam>({
+        orderSn: '',
+        payTimeStart: '',
+        payTimeEnd: '',
+        invoiceTimeStart: '',
+        invoiceTimeEnd: '',
+        orderBy: '',
+        sortBy: '',
       });
-    }
-    function exportExcel() {
-      createConfirm({
-        iconType: 'warning',
-        title: () => h('span', t('common.reminder')),
-        content: () => h('span', t('routes.equity.excelTitle')),
-        onOk: async () => {
-          await DownExport({
-            ...apiData,
-            lang:isEn.value?'en':'cn'
-          });
+      const { createMessage, createConfirm } = useMessage();
+      const [registerAddModal, { openModal: openAddModal }] = useModal();
+      const [registerEditModal, { openModal: openEditModal }] = useModal();
+      const [registerTable, { reload }] = useTable({
+        api: logList,
+        title: t('routes.equity.listTitle'),
+        titleHelpMessage: [
+          t('routes.equity.titleHelpMessage.1'),
+          t('routes.equity.titleHelpMessage.2'),
+          t('routes.equity.titleHelpMessage.3'),
+        ],
+        columns: columns,
+        useSearchForm: true,
+        formConfig: searchForm,
+        showTableSetting: true,
+        showIndexColumn: false,
+        rowKey: 'id',
+        beforeFetch: (params) => {
+          let searchData = {
+            orderSn: params.orderSn,
+            payTimeStart: params.payTime && params.payTime[0],
+            payTimeEnd: params.payTime && params.payTime[1],
+            invoiceTimeStart: params.invoiceTime && params.invoiceTime[0],
+            invoiceTimeEnd: params.invoiceTime && params.invoiceTime[1],
+          };
+          apiData.orderSn = searchData.orderSn;
+          apiData.payTimeStart = searchData.payTimeStart;
+          apiData.payTimeEnd = searchData.payTimeEnd;
+          apiData.invoiceTimeStart = searchData.invoiceTimeStart;
+          apiData.invoiceTimeEnd = searchData.invoiceTimeEnd;
+          return {
+            ...params,
+            ...searchData,
+          };
         },
+        fetchSetting: {
+          pageField: 'pageNum',
+          sizeField: 'pageSize',
+          listField: 'list',
+          totalField: 'total',
+        },
+        canResize: false,
       });
-    }
-    return {
-      registerTable,
-      handleDelete,
-      registerAddModal,
-      registerEditModal,
-      handleInvoice,
-      exportExcel,
-      handleEdit,
-      headleDetails,
-      reload,
-      getCheckPerm,
-      loading,
-      growCardList,
-      t,
-    };
-  },
-});
-</script>
+      function handleDelete(record: Recordable) {
+        console.log('点击了删除', record);
+      }
+      function handleInvoice(record: Recordable) {
+        openAddModal(true, {
+          ...record,
+        });
+      }
+      function handleEdit(record: Recordable) {
+        console.log('record', record);
+      }
+      function headleDetails(record: Recordable) {
+        console.log('record', record);
+        openEditModal(true, {
+          ...record,
+        });
+      }
+      function exportExcel() {
+        createConfirm({
+          iconType: 'warning',
+          title: () => h('span', t('common.reminder')),
+          content: () => h('span', t('routes.equity.excelTitle')),
+          onOk: async () => {
+            await DownExport({
+              ...apiData,
+              lang: isEn.value ? 'en' : 'cn',
+            });
+          },
+        });
+      }
+      return {
+        registerTable,
+        handleDelete,
+        registerAddModal,
+        registerEditModal,
+        handleInvoice,
+        exportExcel,
+        handleEdit,
+        headleDetails,
+        reload,
+        getCheckPerm,
+        loading,
+        growCardList,
+        t,
+      };
+    },
+  });
+</script>

+ 243 - 0
src/views/retailer/AddModal.vue

@@ -0,0 +1,243 @@
+<template>
+  <BasicModal
+    v-bind="$attrs"
+    @register="register"
+    :confirmLoading="loading"
+    :title="fileFlow.title"
+    @visible-change="handleVisibleChange"
+    @cancel="resetFields"
+    @ok="handleSubmit"
+  >
+    <div class="pt-2px pr-3px">
+      <BasicForm @register="registerForm">
+        <template #text="{ model, field }">
+          {{ model[field] }}
+        </template>
+      </BasicForm>
+    </div>
+  </BasicModal>
+</template>
+<script lang="ts">
+  import { defineComponent, ref, nextTick, onMounted, reactive } from 'vue';
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import { BasicForm, FormSchema, useForm } from '/@/components/Form/index';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import { uploadApi } from '/@/api/product/index';
+  import { useI18n } from '/@/hooks/web/useI18n';
+  import { isEmojiCharacter } from '/@/utils';
+  import { checkUserName } from '/@/api/equity';
+  import { addApi, updateApi } from '/@/api/retailer';
+
+  const { t } = useI18n();
+  export default defineComponent({
+    components: { BasicModal, BasicForm },
+    props: {
+      userData: { type: Object },
+    },
+    emits: ['update', 'register'],
+    setup(props, { emit }) {
+      const modelRef = ref({});
+      const loading = ref(false)
+      const fileFlow = reactive({
+        file: null,
+        title: '新增分销商',
+      })
+      const { createMessage } = useMessage();
+      const schemas: FormSchema[] = [
+        {
+          field: 'id',
+          component: 'Input',
+          label: t('routes.retailer.form.name'),
+          show: false,
+          colProps: {
+            span: 24,
+          },
+          // required: true,
+        },
+        {
+          field: 'name',
+          component: 'Input',
+          label: t('routes.retailer.form.name'),
+          required: true,
+          colProps: {
+            span: 24,
+          },
+          componentProps: {
+            maxLength: 50,
+          },
+        },
+        {
+          field: 'nickName',
+          component: 'Input',
+          label: t('routes.retailer.form.nickName'),
+          colProps: {
+            span: 24,
+          },
+          componentProps: {
+            maxLength: 50,
+          },
+        },
+        {
+          field: 'userName',
+          component: 'Input',
+          label: t('routes.retailer.form.userId'),
+          required: true,
+          colProps: {
+            span: 24,
+          },
+          componentProps: {
+            placeholder:t('routes.retailer.form.userName'),
+            maxLength: 50,
+          },
+          rules: [
+            {
+              required: true,
+              // @ts-ignore
+              validator: async (rule, value) => {
+                // const regEmail = /^(.+)@(.+)$/;
+                // const regPos = /^1([38][0-9]|4[579]|5[0-3,5-9]|6[6]|7[0135678]|9[89])\d{8}$/; // 非中文
+                if (!value) {
+                  return Promise.reject(t('routes.retailer.form.userName'));
+                }
+                // if (!(regPos.test(value) || regEmail.test(value))) {
+                //   /* eslint-disable-next-line */
+                // return Promise.reject(t('routes.equity.rules.userName1'));
+                // }
+                // useThrottleFn(handleSplitLeftMenu, 50);
+                const res = await checkUserName({ userName: value });
+                // useThrottleFn(,50)()
+                console.log('validator', res);
+                if (!res) {
+                  return Promise.reject(t('routes.equity.rules.userName2'));
+                }
+                return Promise.resolve();
+              },
+              trigger: 'blur',
+            },
+          ],
+        },
+        // {
+        //   field: 'orderSn',
+        //   component: 'Input',
+        //   label: t('routes.devices.orderSn'),
+        //   colProps: {
+        //     span: 24,
+        //   },
+        // },
+        // {
+        //   field: 'companyId',
+        //   component: 'ApiSelect',
+        //   label: t('routes.devices.companyId'),
+        //   itemProps: {
+        //     validateTrigger: 'blur',
+        //   },
+        //   componentProps: {
+        //     api: allCompanyApi,
+        //     numberToString: true,
+        //     labelField: 'name',
+        //     valueField: 'id',
+        //     immediate: true,
+        //     params: {
+        //       page: 1,
+        //       limit: 1000,
+        //     },
+        //   },
+        //   colProps: {
+        //     span: 24,
+        //   },
+        // },
+      ];
+      const [registerForm, { validate, resetFields, setFieldsValue, updateSchema }] = useForm({
+        labelWidth: 120,
+        schemas,
+        showActionButtonGroup: false,
+        actionColOptions: {
+          span: 24,
+        },
+      });
+      onMounted(() => {});
+      let addListFunc = () => {};
+      const [register, { closeModal }] = useModalInner((data) => {
+        data && onDataReceive(data);
+      });
+      function renderOwnTypeLabel(type: number): string {
+        switch (type) {
+          case 9:
+            return t('routes.product.type.2');
+          case 1:
+            return t('routes.product.type.1');
+          case 10:
+            return t('routes.product.type.3');
+          case 11:
+            return t('routes.product.type.11');
+          default:
+            return '';
+        }
+      }
+      function rendercameraTypeLabel(cameraType: number): string {
+        switch (cameraType) {
+          case 4:
+            return t('routes.devices.cameraName.4');
+          case 1:
+            return t('routes.devices.cameraName.1');
+          case 9:
+            return t('routes.devices.cameraName.9');
+          case 10:
+            return t('routes.devices.cameraName.10');
+          case 6:
+            return t('routes.devices.cameraName.6');
+          default:
+            return '';
+        }
+      }
+      function onDataReceive(data) {
+        modelRef.value = data
+        console.log('renderOwnTypeLabel',data)
+        fileFlow.title = data.id?'编辑分销商':'新增分销商';
+        resetFields();
+        updateSchema({
+          field: 'userName',
+          show: false,
+        })
+        setFieldsValue({
+          ...data,
+        });
+      }
+      const handleSubmit = async () => {
+        loading.value = true
+        try {
+          const params = await validate();
+          let api = params.id ? updateApi : addApi;
+          const res = await api(params);
+          console.log('res', res);
+          closeModal();
+          resetFields();
+          createMessage.success(t('common.optSuccess'));
+          emit('update');
+          loading.value = false
+        } catch (error) {
+          loading.value = false
+          console.log('not passing', error);
+        }
+      };
+      function handleVisibleChange(v) {
+        v && props.userData && nextTick(() => onDataReceive(props.userData));
+      }
+      return {
+        register,
+        rendercameraTypeLabel,
+        renderOwnTypeLabel,
+        schemas,
+        registerForm,
+        model: modelRef,
+        fileFlow,
+        loading,
+        handleVisibleChange,
+        handleSubmit,
+        addListFunc,
+        resetFields,
+        t,
+      };
+    },
+  });
+</script>

+ 207 - 0
src/views/retailer/EditModal.vue

@@ -0,0 +1,207 @@
+<template>
+  <BasicModal
+    v-bind="$attrs"
+    @register="register"
+    :title="t('routes.retailer.setName')"
+    @visible-change="handleVisibleChange"
+    :confirmLoading="loading"
+    @cancel="resetFields"
+    @ok="handleSubmit"
+  >
+    <div class="pt-2px pr-3px">
+      <BasicForm @register="registerForm">
+        <template #text="{ model, field }">
+          {{ model[field] }}
+        </template>
+        <template #type="{ model, field }">
+          {{ renderOwnTypeLabel(Number(model[field])) }}
+        </template>
+      </BasicForm>
+    </div>
+  </BasicModal>
+</template>
+<script lang="ts">
+  import { defineComponent, ref, nextTick, onMounted, reactive, computed } from 'vue';
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import { BasicForm, FormSchema, useForm } from '/@/components/Form/index';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import { useI18n } from '/@/hooks/web/useI18n';
+  import { isEmojiCharacter } from '/@/utils';
+  import { useUserStore } from '/@/store/modules/user';
+  import { addIncrementNum } from '/@/api/retailer';
+
+  const { t } = useI18n();
+  export default defineComponent({
+    components: { BasicModal, BasicForm },
+    props: {
+      userData: { type: Object },
+    },
+    emits: ['update', 'register'],
+    setup(props, { emit }) {
+      const modelRef = ref({});
+      const loading = ref(false);
+      const fileFlow = reactive({
+        file: null,
+      });
+      const userStore = useUserStore();
+      const { getUserInfoAction } = userStore
+      const isDev = computed(() => userStore.getSystemEnv);
+      const agent = computed(() => userStore.getAgent);
+      const { createMessage } = useMessage();
+      const schemas: FormSchema[] = [
+        {
+          field: 'id',
+          component: 'Input',
+          label: t('routes.product.types'),
+          show: false,
+          colProps: {
+            span: 24,
+          },
+        },
+        {
+          field: 'name',
+          component: 'Input',
+          label: '当前分销商',
+          slot: 'text',
+          colProps: {
+            span: 24,
+          },
+        },
+        {
+          field: 'majorAddNum',
+          component: 'InputNumber',
+          label: t('routes.retailer.majorAddNum', {value: agent.value.majorSubNum}),
+          defaultValue: 0,
+          colProps: {
+            span: 22,
+          },
+        },
+        {
+          field: 'highAddNum',
+          component: 'InputNumber',
+          label: t('routes.retailer.highAddNum', {value: agent.value.highSubNum}),
+          defaultValue: 0,
+          ifShow: isDev.value,
+          colProps: {
+            span: 22,
+          },
+        },
+        {
+          field: 'downAddNum',
+          component: 'InputNumber',
+          label: t('routes.retailer.downAddNum', {value: agent.value.downSubNum}),
+          defaultValue: 0,
+          colProps: {
+            span: 22,
+          },
+        },
+      ];
+      const [registerForm, { validate, resetFields, setFieldsValue, updateSchema }] = useForm({
+        labelWidth: 300,
+        schemas,
+        showActionButtonGroup: false,
+        actionColOptions: {
+          span: 24,
+        },
+      });
+      onMounted(() => {});
+      let addListFunc = () => {};
+      const [register, { closeModal }] = useModalInner((data) => {
+        data && onDataReceive(data);
+      });
+      function renderOwnTypeLabel(type: number): string {
+        switch (type) {
+          case 9:
+            return t('routes.product.type.2');
+          case 1:
+            return t('routes.product.type.1');
+          case 10:
+            return t('routes.product.type.3');
+          case 11:
+            return t('routes.product.type.11');
+          default:
+            return '';
+        }
+      }
+      function onDataReceive(data) {
+        modelRef.value = data;
+        console.log('onDataReceive', data);
+        resetFields();
+        setTimeout(() => {
+          updateSchema([
+            {
+              field: 'majorAddNum',
+              label: t('routes.retailer.majorAddNum', {value: agent.value.majorSubNum}),
+              componentProps: {
+                max: agent.value.majorSubNum,
+                min: 0,
+              },
+            },
+            {
+              field: 'highAddNum',
+              label: t('routes.retailer.highAddNum', {value: agent.value.highSubNum}),
+              componentProps: {
+                max: agent.value.highSubNum,
+                min: 0,
+              },
+              ifShow: isDev.value,
+            },
+            {
+              field: 'downAddNum',
+              label: t('routes.retailer.downAddNum', {value: agent.value.downSubNum}),
+              componentProps: {
+                max: agent.value.downSubNum,
+                min: 0,
+              },
+            },
+          ]);
+        }, 500);
+        setFieldsValue({
+          ...data,
+          downAddNum: 0,
+          majorAddNum: 0,
+          highAddNum: 0,
+        });
+      }
+      const handleSubmit = async () => {
+        loading.value = true;
+        try {
+          const params = await validate();
+          console.log('modelRef', params);
+          const apiData = {
+            // ...modelRef.value,
+            ...(params as any),
+          };
+          console.log('res', apiData);
+          const res = await addIncrementNum(apiData);
+          console.log('res', res);
+          closeModal();
+          resetFields();
+          createMessage.success(t('common.optSuccess'));
+          emit('update');
+          getUserInfoAction();
+          loading.value = false;
+        } catch (error) {
+          console.log('not passing', error);
+          loading.value = false;
+        }
+      };
+      function handleVisibleChange(v) {
+        v && props.userData && nextTick(() => onDataReceive(props.userData));
+      }
+      return {
+        register,
+        renderOwnTypeLabel,
+        schemas,
+        loading,
+        registerForm,
+        fileFlow,
+        handleVisibleChange,
+        handleSubmit,
+        addListFunc,
+        resetFields,
+        t,
+      };
+    },
+  });
+</script>

+ 251 - 0
src/views/retailer/index.vue

@@ -0,0 +1,251 @@
+<template>
+  <PageWrapper contentBackground>
+    <div class="desc-wrap-BasicTable">
+      <BasicTable @register="registerTimeTable">
+        <template #toolbar>
+          <a-button type="primary" @click="handleAdd"> {{t('routes.retailer.add')}}</a-button>
+        </template>
+        <template #action="{ record }">
+          <TableAction
+            stopButtonPropagation
+            :actions="[
+              {
+                label: t('routes.retailer.setName'),
+                //icon: 'la:file-invoice-dollar',
+                onClick: handleEdit.bind(null, record),
+              },
+              {
+                label: t('common.edit'),
+                //icon: 'la:file-invoice-dollar',
+                onClick: headleDetails.bind(null, record),
+              },
+            ]"
+          />
+        </template>
+      </BasicTable>
+    </div>
+    <AddModal @update="reload" @register="registerAddModal" />
+    <EditModal @register="registerEditModal" @update="reload" />
+  </PageWrapper>
+</template>
+<script lang="ts">
+  import { defineComponent, reactive, h, computed } from 'vue';
+  import { BasicTable, useTable, FormProps, TableAction, BasicColumn } from '/@/components/Table';
+  import { PageWrapper } from '/@/components/Page';
+  import { Divider, Card, Empty, Descriptions, Steps, Tabs } from 'ant-design-vue';
+  import { listApi, banOrUnBan } from '/@/api/retailer';
+  import { useModal } from '/@/components/Modal';
+  import { useI18n } from '/@/hooks/web/useI18n';
+  import AddModal from './AddModal.vue';
+  import EditModal from './EditModal.vue';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import { Switch } from 'ant-design-vue';
+  import { Time } from '/@/components/Time';
+  import { useUserStore } from '/@/store/modules/user';
+  import { usePermissionStore } from '/@/store/modules/permission';
+  export default defineComponent({
+    components: {
+      BasicTable,
+      AddModal,
+      EditModal,
+      TableAction,
+      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 { t } = useI18n();
+      const permissionStore = usePermissionStore();
+      const { getCheckPerm } = permissionStore;
+      const searchInfo = reactive<Recordable>({
+        type: 1,
+      });
+      const [registerAddModal, { openModal: openAddModal }] = useModal();
+      const [registerEditModal, { openModal: openEditModal }] = useModal();
+      const userStore = useUserStore();
+      const isDev = computed(() => userStore.getSystemEnv);
+      const { createMessage } = useMessage();
+
+      const refundTimeTableSchema: BasicColumn[] = [
+        {
+          title: t('routes.retailer.name'),
+          width: 120,
+          dataIndex: 'name',
+        },
+        {
+          title: t('routes.retailer.form.nickName'),
+          width: 120,
+          dataIndex: 'nickName',
+        },
+        {
+          title: t('routes.retailer.form.userId'),
+          width: 100,
+          dataIndex: 'userName',
+        },
+        {
+          title: t('routes.retailer.majorTotalNum'),
+          width: 150,
+          dataIndex: 'majorTotalNum',
+          customRender: ({ record }) => {
+            return record.majorTotalNum - record.majorUseNum;
+          }
+        },
+        {
+          title: t('routes.retailer.highTotalNum'),
+          width: 150,
+          dataIndex: 'highTotalNum',
+          ifShow: isDev.value,
+          customRender: ({ record }) => {
+            return record.highTotalNum - record.highUseNum;
+          }
+        },
+        {
+          title: t('routes.retailer.downTotalNum'),
+          width: 150,
+          dataIndex: 'downTotalNum',
+          customRender: ({ record }) => {
+            return record.downTotalNum - record.downUseNum;
+          }
+        },
+        {
+          title: t('routes.retailer.createName'),
+          width: 120,
+          dataIndex: 'createName',
+        },
+        {
+          title: t('routes.retailer.createTime'),
+          width: 150,
+          dataIndex: 'createTime',
+          customRender: ({ record }) => {
+            return (
+              record.createTime &&
+              h(Time, {
+                value: record.createTime,
+                mode: 'datetime',
+              })
+            );
+          },
+        },
+        {
+          title: t('common.state'),
+          dataIndex: 'status',
+          width: 80,
+          customRender: ({ record }) => {
+            if (!Reflect.has(record, 'pendingStatus')) {
+              record.pendingStatus = false;
+            }
+            return h(Switch, {
+              checked: record.status == '1',
+              checkedChildren: t('routes.system.enable'),
+              unCheckedChildren: t('routes.system.stopUsing'),
+              loading: false,
+              onChange: async (checked: boolean) => {
+                record.pendingStatus = true;
+                const newStatus = checked ? '1' : '0';
+                await banOrUnBan({ id: record.id, status: newStatus });
+                if (checked) {
+                  Reflect.set(record, 'status', newStatus);
+                } else {
+                  Reflect.set(record, 'status', newStatus);
+                }
+                reload();
+                createMessage.success(t('common.optSuccess'));
+              },
+            });
+          },
+        },
+      ];
+      const searchForm: Partial<FormProps> = {
+        labelWidth: 100,
+        autoSubmitOnEnter: true,
+        schemas: [
+          {
+            field: 'version',
+            label: t('routes.retailer.name'),
+            component: 'Input',
+            componentProps: {
+              maxLength: 100,
+            },
+            colProps: {
+              xl: 6,
+              xxl: 6,
+            },
+          },
+        ],
+      };
+      const [registerTimeTable, { reload }] = useTable({
+        api: listApi,
+        columns: refundTimeTableSchema,
+        useSearchForm: true,
+        formConfig: searchForm,
+        showTableSetting: true,
+        showIndexColumn: false,
+        rowKey: 'id',
+        fetchSetting: {
+          pageField: 'pageNum',
+          sizeField: 'pageSize',
+          listField: 'list',
+          totalField: 'total',
+        },
+        searchInfo: searchInfo,
+        // beforeFetch:(T)=>{
+        //   T.type = searchInfo.type
+        //   console.log('beforeFetch',T,searchInfo)
+        //   return T
+        // },
+        actionColumn: {
+          width: 150,
+          title: t('common.operating'),
+          dataIndex: 'action',
+          slots: { customRender: 'action' },
+        },
+        canResize: true,
+      });
+      function tabChange(val: string) {
+        console.log('tabChange', val);
+        reload();
+      }
+      async function handleDelete(record: Recordable) {
+        console.log('点击了删除', record);
+        await DelAndUpload({ id: record.id });
+        createMessage.success(t('common.optSuccess'));
+        reload();
+      }
+      function handleOpen(record: Recordable) {
+        console.log('点击了启用', record);
+      }
+      function handleAdd() {
+        openAddModal(true, {});
+      }
+      function headleDetails(record: Recordable) {
+        openAddModal(true, record);
+      }
+      function handleEdit(record: Recordable) {
+        openEditModal(true, record);
+      }
+      return {
+        registerTimeTable,
+        handleDelete,
+        handleOpen,
+        tabChange,
+        reload,
+        registerAddModal,
+        registerEditModal,
+        openAddModal,
+        handleEdit,
+        getCheckPerm,
+        t,
+        searchInfo,
+        handleAdd,
+        headleDetails,
+      };
+    },
+  });
+</script>

+ 4 - 3
types/store.d.ts

@@ -58,10 +58,10 @@ export interface UserInfo {
   desc?: string;
   homePath?: string;
   roleId: number;
-  roles: number[]; //RoleInfo[];
+  roles: number[] | string[]; //RoleInfo[];
   availableSpace: number;
   cameraCount: number;
-  city:  string |null;
+  city: string | null;
   country: string;
   downloadNum: 57;
   downloadNumTotal: number;
@@ -87,7 +87,7 @@ export interface UserInfo {
   totalSpace: string | number | null;
   usedSpace: string | number | null;
   userName: string;
-  agent?:UserAgent;
+  agent?: UserAgent;
 }
 export interface UserAgent {
   createTime: string;
@@ -107,6 +107,7 @@ export interface UserAgent {
   sysUserId: number;
   updateTime: string;
   userName: string;
+  parentId: string | number | null;
 }
 export interface BeforeMiniState {
   menuCollapsed?: boolean;