瀏覽代碼

feat(组件): 图表数据组装

tangning 2 年之前
父節點
當前提交
e270c66107

+ 1 - 1
.env.development

@@ -19,4 +19,4 @@ VITE_GLOB_API_URL=
 VITE_GLOB_UPLOAD_URL=/upload
 
 # Interface prefix
-VITE_GLOB_API_URL_PREFIX=/manage
+VITE_GLOB_API_URL_PREFIX=

+ 55 - 10
src/api/operate/index.ts

@@ -42,22 +42,67 @@ enum Api {
   rebuildScene = '/manage/service/manage/scene/rebuildScene',
   overallList = '/manage/service/manage/overall/list',
   overallDelete = '/manage/service/manage/overall/delete',
+  agentAuditList = '/manage/service/manage/agentAudit/list',
+  agentAuditHandle = '/manage/service/manage/agentAudit/handle',
+  sceneApplyList = '/manage/service/manage/sceneApply/list',
+  sceneApplyHandle = '/manage/service/manage/sceneApply/handle',
+  
 }
 
 /**
  * @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 agentAuditList = (params: PageParams) =>
+ defHttp.post<RentListGetResultModel>({
+   url: Api.agentAuditList,
+   params: params,
+   // data: params,
+   headers: {
+     // @ts-ignore
+     ignoreCancelToken: true,
+   },
+ });
+ export const agentAuditHandle = (params: PageParams) =>
+ defHttp.post<RentListGetResultModel>({
+   url: Api.agentAuditHandle,
+   params: params,
+   // data: params,
+   headers: {
+     // @ts-ignore
+     ignoreCancelToken: true,
+   },
+ });
+ export const sceneApplyList = (params: PageParams) =>
+ defHttp.post<RentListGetResultModel>({
+   url: Api.sceneApplyList,
+   params: params,
+   // data: params,
+   headers: {
+     // @ts-ignore
+     ignoreCancelToken: true,
+   },
+ });
+ export const sceneApplyHandle = (params: PageParams) =>
+ defHttp.post<RentListGetResultModel>({
+   url: Api.sceneApplyHandle,
+   params: params,
+   // data: params,
+   headers: {
+     // @ts-ignore
+     ignoreCancelToken: true,
+   },
+ });
+ export const ListApi = (params: PageParams) =>
+ defHttp.post<RentListGetResultModel>({
+   url: Api.pageList,
+   params: params,
+   // data: params,
+   headers: {
+     // @ts-ignore
+     ignoreCancelToken: true,
+   },
+ });
 //显示隐藏
 export const NewDisplay = (params: PageParams) =>
   defHttp.post<RentListGetResultModel>({

+ 47 - 1
src/api/statistics/index.ts

@@ -8,6 +8,10 @@ enum Api {
   buryPointDlt = '/manage/service/manage/buryPoint/delete',
   userTotal = '/manage/service/manage/data/userTotal',
   orderTotal = '/manage/service/manage/data/orderTotal',
+  userTrend = '/manage/service/manage/data/userTrend',
+  orderTrend = '/manage/service/manage/data/orderTrend',
+  cameraTrend = '/manage/service/manage/data/cameraTrend',
+  sceneTrend = '/manage/service/manage/data/sceneTrend',
 }
 
 /**
@@ -53,6 +57,14 @@ export const buryPointList = (params: PageParams) =>
       ignoreCancelToken: true,
     },
   });
+  export const userTrend = () =>
+  defHttp.get<Result>({
+    url: Api.userTrend,
+    headers: {
+      // @ts-ignore
+      ignoreCancelToken: true,
+    },
+  });
   export const orderTotal = () =>
   defHttp.get<Result>({
     url: Api.orderTotal,
@@ -60,4 +72,38 @@ export const buryPointList = (params: PageParams) =>
       // @ts-ignore
       ignoreCancelToken: true,
     },
-  });
+  });
+  
+  export const orderTrend = (params) =>
+  defHttp.get<Result>({
+    url: Api.orderTrend,
+    params,
+    data: params,
+    headers: {
+      // @ts-ignore
+      ignoreCancelToken: true,
+    },
+  });
+  
+  export const cameraTrend = (params) =>
+  defHttp.get<Result>({
+    url: Api.cameraTrend,
+    params,
+    data: params,
+    headers: {
+      // @ts-ignore
+      ignoreCancelToken: true,
+    },
+  });
+  
+  export const sceneTrend = (params) =>
+  defHttp.get<Result>({
+    url: Api.sceneTrend,
+    params,
+    data: params,
+    headers: {
+      // @ts-ignore
+      ignoreCancelToken: true,
+    },
+  });
+  

+ 4 - 4
src/views/anchor/index.vue

@@ -68,11 +68,11 @@
       const columns: BasicColumn[] = [
         {
           title: '应用名称',
-          dataIndex: 'nickName',
+          dataIndex: 'applicationName',
         },
         {
           title: '应用key',
-          dataIndex: 'userName',
+          dataIndex: 'applicationKey',
         },
         {
           title: '操作',
@@ -174,9 +174,9 @@
         createConfirm({
           iconType: 'warning',
           title: '警告',
-          content: `此操作将对${record.userName}进行删除, 是否继续?`,
+          content: `此操作将对${record.applicationName}进行删除, 是否继续?`,
           onOk: async () => {
-            await buryPointDlt({ userId: record.id });
+            await buryPointDlt({ id: record.id });
             reload();
             createMessage.success(t('common.optSuccess'));
           },

+ 23 - 14
src/views/operate/agent.vue

@@ -2,14 +2,14 @@
   <PageWrapper  contentBackground>
     <template #footer>
       <a-tabs v-model:activeKey="state" @change="changeTable">
-        <a-tab-pane :key="1" tab="待处理" />
-        <a-tab-pane :key="0" tab="已处理" />
+        <a-tab-pane :key="0" tab="待处理" />
+        <a-tab-pane :key="1" tab="已处理" />
       </a-tabs>
     </template>
 
     <div class="pt-4 m-4 desc-wrap">
       <BasicTable @register="registerTimeTable" >
-        
+
         <template #action="{ record }">
           <TableAction
             stopButtonPropagation
@@ -17,7 +17,7 @@
               {
                 label: '处理',
                 icon: 'icon-park-outline:door-handle',
-                ifShow:record.state == 1,
+                ifShow:record.state == 0,
                 onClick: handleWithdraw.bind(null, record),
               },
             ]"
@@ -33,11 +33,11 @@
   import { BasicTable, useTable, TableAction, FormProps } from '/@/components/Table';
   import { PageWrapper } from '/@/components/Page';
   import { Divider, Card, Empty, Descriptions, Steps, Tabs } from 'ant-design-vue';
-  import { intercomMessageList, intercomMessageHandle } from '/@/api/operate'
+  import { agentAuditList, agentAuditHandle } from '/@/api/operate'
   import { useI18n } from '/@/hooks/web/useI18n';
   import addMessgeModal from './components/message/addModal.vue'
   import { useModal } from '/@/components/Modal';
-  import { refundTimeTableSchema, refundTimeTableData } from './data';
+  import { agentSchema, refundTimeTableData } from './data';
   export default defineComponent({
     components: {
       BasicTable,
@@ -57,12 +57,12 @@
     setup() {
       const { t } = useI18n();
       const [register, { openModal }] = useModal();
-      const state = ref<number>(1); //未处理,0已处理(默认1)
+      const state = ref<number>(0); //未处理,0已处理(默认1)
       const searchForm: Partial<FormProps> = {
         labelWidth: 100,
         schemas: [
           {
-            field: 'sceneName',
+            field: 'payTime',
             label: '提交时间',
             component: 'RangePicker',
             componentProps: {
@@ -76,8 +76,8 @@
             },
           },
           {
-            field: 'type',
-            label: t('routes.operate.newsTitle'),
+            field: 'companyName',
+            label: '公司名称',
             component: 'Input',
             colProps: {
               xl: 6,
@@ -87,7 +87,7 @@
         ],
       };
       const [registerTimeTable,{reload}] = useTable({
-        api: intercomMessageList,
+        api: agentAuditList,
         // title: '',
         rowKey: 'id',
         fetchSetting: {
@@ -96,13 +96,22 @@
           listField: 'list',
           totalField: 'total',
         },
-        searchInfo: { state },
-        columns: refundTimeTableSchema,
+        searchInfo: { handleState:state },
+        columns: agentSchema,
         formConfig: searchForm,
+        useSearchForm: true,
         dataSource: refundTimeTableData,
+        showTableSetting: true,
         showIndexColumn: false,
         // pagination: { pageSize: 20 },
-        scroll: { y: 300 },
+        beforeFetch:(params)=>{
+          return {
+            ...params,
+            startTime: params.payTime && params.payTime[0],
+            endTime: params.payTime && params.payTime[1],
+          }
+        },
+        // scroll: { y: 300 },
         actionColumn: {
           width: 100,
           title: '操作',

+ 123 - 0
src/views/operate/components/agent/addModal.vue

@@ -0,0 +1,123 @@
+<template>
+  <BasicModal
+    v-bind="$attrs"
+    @register="register"
+    title="处理申请"
+    @visible-change="handleVisibleChange"
+    okText="完成处理"
+    @cancel="resetFields"
+    @ok="handleSubmit"
+  >
+    <div class="pt-2px pr-3px">
+      <BasicForm @register="registerForm" :model="model" >
+        <template #text="{ model, field }">
+          {{ model[field]  }}
+        </template>
+      </BasicForm>
+    </div>
+  </BasicModal>
+</template>
+<script lang="ts">
+  import { defineComponent, ref, nextTick, onMounted, reactive, h } 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 { newAddNews, addOrUpdate } from '/@/api/operate'
+  import { useI18n } from '/@/hooks/web/useI18n';
+  import { Tinymce } from '/@/components/Tinymce/index';
+  import { agentAuditHandle } from '/@/api/operate'
+
+  const { t } = useI18n();
+  export default defineComponent({
+    components: { BasicModal, BasicForm },
+    props: {
+      userData: { type: Object },
+    },
+    emits: ['update', 'register'],
+    setup(props, { emit }) {
+      const modelRef = ref({});
+      const fileFlow = reactive({
+        coverImageUrl:''
+      })
+      const { createMessage } = useMessage();
+      const schemas: FormSchema[] = [
+        {
+          field: 'id',
+          component: 'Input',
+          show:false,
+          label: 'id',
+          required: false,
+        },{
+            field: 'noteContent',
+            component: 'InputTextArea',
+            required: true,
+            label: '备注',
+            componentProps: {
+              maxLength: 200,
+              placeholder: '请备注处理结果',
+            },
+            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) => {
+        data && onDataReceive(data);
+      });
+      async function onDataReceive(data) {
+        modelRef.value = data
+        resetFields();
+        setFieldsValue(data);
+      }
+      function NewTypeChange(val){
+        console.log('NewTypeChange',val)
+        updateSchema([
+          { field: 'content', ifShow:val == 2,},
+          { field: 'newsUrl', ifShow:val != 2,},
+        ])
+      } 
+      const handleSubmit = async () => {
+        const params = await validate();
+        const apiData = {
+          ...params as any,
+        }
+        try {
+          await agentAuditHandle(apiData);
+          closeModal();
+          resetFields();
+          createMessage.success(t('common.optSuccess'));
+          emit('update');
+        } catch (error) {
+          console.log('not passing', error);
+        }
+      };
+      function handleVisibleChange(v) {
+        v && props.userData && nextTick(() => onDataReceive(props.userData));
+      }
+      return {
+        register,
+        schemas,
+        registerForm,
+        model: modelRef,
+        fileFlow,
+        NewTypeChange,
+        handleVisibleChange,
+        handleSubmit,
+        addListFunc,
+        resetFields,
+        t,
+      };
+    },
+  });
+</script>

+ 123 - 0
src/views/operate/components/scene/addModal.vue

@@ -0,0 +1,123 @@
+<template>
+  <BasicModal
+    v-bind="$attrs"
+    @register="register"
+    title="处理申请"
+    @visible-change="handleVisibleChange"
+    okText="完成处理"
+    @cancel="resetFields"
+    @ok="handleSubmit"
+  >
+    <div class="pt-2px pr-3px">
+      <BasicForm @register="registerForm" :model="model" >
+        <template #text="{ model, field }">
+          {{ model[field]  }}
+        </template>
+      </BasicForm>
+    </div>
+  </BasicModal>
+</template>
+<script lang="ts">
+  import { defineComponent, ref, nextTick, onMounted, reactive, h } 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 { newAddNews, addOrUpdate } from '/@/api/operate'
+  import { useI18n } from '/@/hooks/web/useI18n';
+  import { Tinymce } from '/@/components/Tinymce/index';
+  import { sceneApplyHandle } from '/@/api/operate'
+
+  const { t } = useI18n();
+  export default defineComponent({
+    components: { BasicModal, BasicForm },
+    props: {
+      userData: { type: Object },
+    },
+    emits: ['update', 'register'],
+    setup(props, { emit }) {
+      const modelRef = ref({});
+      const fileFlow = reactive({
+        coverImageUrl:''
+      })
+      const { createMessage } = useMessage();
+      const schemas: FormSchema[] = [
+        {
+          field: 'id',
+          component: 'Input',
+          show:false,
+          label: 'id',
+          required: false,
+        },{
+            field: 'noteContent',
+            component: 'InputTextArea',
+            required: true,
+            label: '备注',
+            componentProps: {
+              maxLength: 200,
+              placeholder: '请备注处理结果',
+            },
+            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) => {
+        data && onDataReceive(data);
+      });
+      async function onDataReceive(data) {
+        modelRef.value = data
+        resetFields();
+        setFieldsValue(data);
+      }
+      function NewTypeChange(val){
+        console.log('NewTypeChange',val)
+        updateSchema([
+          { field: 'content', ifShow:val == 2,},
+          { field: 'newsUrl', ifShow:val != 2,},
+        ])
+      } 
+      const handleSubmit = async () => {
+        const params = await validate();
+        const apiData = {
+          ...params as any,
+        }
+        try {
+          await sceneApplyHandle(apiData);
+          closeModal();
+          resetFields();
+          createMessage.success(t('common.optSuccess'));
+          emit('update');
+        } catch (error) {
+          console.log('not passing', error);
+        }
+      };
+      function handleVisibleChange(v) {
+        v && props.userData && nextTick(() => onDataReceive(props.userData));
+      }
+      return {
+        register,
+        schemas,
+        registerForm,
+        model: modelRef,
+        fileFlow,
+        NewTypeChange,
+        handleVisibleChange,
+        handleSubmit,
+        addListFunc,
+        resetFields,
+        t,
+      };
+    },
+  });
+</script>

+ 112 - 1
src/views/operate/data.tsx

@@ -1,5 +1,6 @@
 import { BasicColumn } from '/@/components/Table/src/types/table';
-
+import { Time } from '/@/components/Time';
+import { h } from 'vue';
 import { Badge } from 'ant-design-vue';
 
 export const refundTimeTableSchema: BasicColumn[] = [
@@ -28,6 +29,116 @@ export const refundTimeTableSchema: BasicColumn[] = [
   },
 ];
 
+export const agentSchema: BasicColumn[] = [
+  {
+    title: '公司名称',
+    width: 150,
+    dataIndex: 'name',
+  },
+  {
+    title: '公司地址',
+    width: 150,
+    dataIndex: 'address',
+  },
+  {
+    title: '门店类型',
+    width: 150,
+    dataIndex: 'country',
+    customRender: ({ record }) => {
+      return <Badge status='success' text={record.type == 1?'线下':'线上'} />;
+    },
+  },
+  {
+    title: '门店地址',
+    width: 150,
+    dataIndex: 'storeAddress',
+  },
+  {
+    title: '申请人姓名',
+    width: 150,
+    dataIndex: 'surName',
+    customRender: ({ record }) => {
+      return record.surName + record.userName
+    },
+  },
+  {
+    title: '申请人电话',
+    width: 150,
+    dataIndex: 'phone',
+  },
+  {
+    title: '申请人邮箱',
+    width: 150,
+    dataIndex: 'email',
+  },
+  {
+    title: '提交时间',
+    width: 150,
+    dataIndex: 'createTime',
+    customRender: ({ record }) => {
+      return (
+        record.createTime &&
+        h(Time, {
+          value: record.createTime,
+          mode: 'datetime',
+        })
+      );
+    },
+  },
+  {
+    title: '处理备注',
+    width: 150,
+    dataIndex: 'noteContent',
+  },
+];
+
+export const DMegaSchema: BasicColumn[] = [
+  {
+    title: '公司名称',
+    width: 150,
+    dataIndex: 'name',
+  },
+  {
+    title: '行业',
+    width: 150,
+    dataIndex: 'content',
+  },
+  {
+    title: '国家与地区',
+    width: 150,
+    dataIndex: 'country',
+  },
+  {
+    title: '申请人姓名',
+    width: 150,
+    dataIndex: 'content',
+  },
+  {
+    title: '申请人电话',
+    width: 150,
+    dataIndex: 'content',
+  },
+  {
+    title: '申请人邮箱',
+    width: 150,
+    dataIndex: 'content',
+  },
+  {
+    title: '提交时间',
+    width: 150,
+    dataIndex: 'createTime',
+    customRender: ({ record }) => {
+      return (
+        record.createTime &&
+        h(Time, {
+          value: record.createTime,
+          mode: 'datetime',
+        })
+      );
+    },
+  },
+];
+
 export const refundTimeTableData: any[] = [
   {
     t1: '2017-10-01 14:10',

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

@@ -100,6 +100,7 @@
         columns: refundTimeTableSchema,
         formConfig: searchForm,
         dataSource: refundTimeTableData,
+        showTableSetting: true,
         showIndexColumn: false,
         // pagination: { pageSize: 20 },
         scroll: { y: 300 },

+ 36 - 15
src/views/operate/sceneList.vue

@@ -2,22 +2,21 @@
   <PageWrapper  contentBackground>
     <template #footer>
       <a-tabs v-model:activeKey="state" @change="changeTable">
-        <a-tab-pane :key="1" tab="待处理" />
-        <a-tab-pane :key="0" tab="已处理" />
+        <a-tab-pane :key="0" tab="待处理" />
+        <a-tab-pane :key="1" tab="已处理" />
       </a-tabs>
     </template>
 
     <div class="pt-4 m-4 desc-wrap">
       <BasicTable @register="registerTimeTable" >
-        
         <template #action="{ record }">
           <TableAction
             stopButtonPropagation
             :actions="[
               {
-                label: '处理',
+                label: '发送',
                 icon: 'icon-park-outline:door-handle',
-                ifShow:record.state == 1,
+                ifShow:record.state == 0,
                 onClick: handleWithdraw.bind(null, record),
               },
             ]"
@@ -34,10 +33,12 @@
   import { PageWrapper } from '/@/components/Page';
   import { Divider, Card, Empty, Descriptions, Steps, Tabs } from 'ant-design-vue';
   import { intercomMessageList, intercomMessageHandle } from '/@/api/operate'
+  import { sceneApplyList } from '/@/api/operate'
   import { useI18n } from '/@/hooks/web/useI18n';
   import addMessgeModal from './components/message/addModal.vue'
   import { useModal } from '/@/components/Modal';
-  import { refundTimeTableSchema, refundTimeTableData } from './data';
+  import { DMegaSchema, refundTimeTableData } from './data';
+  import { useMessage } from '/@/hooks/web/useMessage';
   export default defineComponent({
     components: {
       BasicTable,
@@ -56,13 +57,14 @@
     },
     setup() {
       const { t } = useI18n();
+      const { createConfirm, createMessage } = useMessage();
       const [register, { openModal }] = useModal();
-      const state = ref<number>(1); //未处理,0已处理(默认1)
+      const state = ref<number>(0); //未处理,0已处理(默认1)
       const searchForm: Partial<FormProps> = {
         labelWidth: 100,
         schemas: [
           {
-            field: 'sceneName',
+            field: 'payTime',
             label: '提交时间',
             component: 'RangePicker',
             componentProps: {
@@ -76,8 +78,8 @@
             },
           },
           {
-            field: 'type',
-            label: t('routes.operate.newsTitle'),
+            field: 'companyName',
+            label: '公司名称',
             component: 'Input',
             colProps: {
               xl: 6,
@@ -87,7 +89,7 @@
         ],
       };
       const [registerTimeTable,{reload}] = useTable({
-        api: intercomMessageList,
+        api: sceneApplyList,
         // title: '',
         rowKey: 'id',
         fetchSetting: {
@@ -96,13 +98,22 @@
           listField: 'list',
           totalField: 'total',
         },
-        searchInfo: { state },
-        columns: refundTimeTableSchema,
+        searchInfo: { handleState:state },
+        columns: DMegaSchema,
         formConfig: searchForm,
+        useSearchForm: true,
         dataSource: refundTimeTableData,
+        showTableSetting: true,
         showIndexColumn: false,
         // pagination: { pageSize: 20 },
-        scroll: { y: 300 },
+        // scroll: { y: 300 },
+        beforeFetch:(params)=>{
+          return {
+            ...params,
+            startTime: params.payTime && params.payTime[0],
+            endTime: params.payTime && params.payTime[1],
+          }
+        },
         actionColumn: {
           width: 100,
           title: '操作',
@@ -116,7 +127,17 @@
         reload();
       }
       async function handleWithdraw(record: Recordable) {
-        openModal(true,record)
+        createConfirm({
+          iconType: 'info',
+          title: '发送邮件',
+          content: `此操作将对${record.applicationName}进行删除, 是否继续?`,
+          onOk: async () => {
+            await buryPointDlt({ id: record.id });
+            reload();
+            createMessage.success(t('common.optSuccess'));
+          },
+        });
+        // openModal(true,record)
       }
       return {
         registerTimeTable,

+ 49 - 18
src/views/statistics/camera/index.vue

@@ -1,26 +1,57 @@
 <template>
   <div class="p-4">
-    <GrowCard :loading="loading" :list="growCardList" class="enter-y" />
-    <!-- <div class="md:flex !my-4 enter-y">
-      <lineEcharts class="md:w-1/2 w-full !md:mt-0 !mt-4 !md:mr-4" name="chartRef1" :loading="loading"  />
-      <lineEcharts2  name="chartRef2" class="md:w-1/2 mx-4 w-full" :loading="loading" />
-    </div> -->
-    <lineEcharts2 class="!my-4 enter-y" :loading="loading" />
-    <!-- <div class="md:flex enter-y">
-      <VisitRadar class="md:w-1/3 w-full" :loading="loading" />
-      <VisitSource class="md:w-1/3 !md:mx-4 !md:my-0 !my-4 w-full" :loading="loading" />
-      <SalesProductPie class="md:w-1/3 w-full" :loading="loading" />
-    </div> -->
+    <!-- <GrowCard :loading="loading" :list="growCardList" class="enter-y" /> -->
+    <orderEchart title="相机出库数量统计" class="!my-4 enter-y"  @change="Search" :echartData="echartData" :loading="loading" />
   </div>
 </template>
 <script lang="ts" setup>
-  import { ref } from 'vue';
-  import { growCardList } from '../data';
-  import GrowCard from '../components/GrowCard.vue';
-  import lineEcharts2 from '../components/lineEcharts2.vue';
+  import { ref,reactive,onMounted } from 'vue';
+  // import { growCardList } from '../data';
+  import orderEchart from '../components/orderEchart.vue';
+  import { cameraTrend } from '/@/api/statistics/index';
   const loading = ref(true);
-
-  setTimeout(() => {
+  const echartData = reactive({
+    xdata:[],
+    nameList:['四维看看','思维看见','四维深时'],
+    downOrder:[],
+    incrementOrder:[],
+    partOrder:[],
+    echartTypr:'bar',
+  })
+  const SearchData = reactive({
+    startTime:'',
+    endTime:'',
+    dataType:0,
+    type:0,
+  })
+  async function getList() {
+    let downlist = [],xdata = []
+    loading.value = true;
+    const {kjList,kkList,ssList} = await cameraTrend(SearchData);
+    kjList.map(ele => {
+      xdata.push(ele.groupKey)
+      downlist.push(ele.count)
+    })
+    echartData.xdata = xdata
+    echartData.downOrder =kkList.map(ele => ele.count)
+    echartData.incrementOrder =  kjList &&kjList.map(ele => ele.count)|| []
+    echartData.partOrder = ssList &&ssList.map(ele => ele.count)|| []
+    console.log('echartData',echartData)
     loading.value = false;
-  }, 1500);
+  }
+  onMounted(() => {
+    getList()
+  });
+  function Search(val){
+    const {startTime,endTime,dataType,type} = val
+    console.log('params',val)
+    SearchData.startTime = startTime
+    SearchData.endTime = endTime
+    SearchData.dataType = dataType
+    SearchData.type = type
+    getList()
+  }
+  // setTimeout(() => {
+  //   loading.value = false;
+  // }, 1500);
 </script>

+ 99 - 0
src/views/statistics/components/condition.vue

@@ -0,0 +1,99 @@
+<template>
+  <div class="condition">
+    <div class="selct" style="display: inline-block">
+      <!-- <span style="margin-right:15px"></span> -->
+      <Select
+        v-model:value="type"
+        style="width: 100px; margin-right: 30px"
+        placeholder="请选择类型"
+        :options="typeOptions"
+        @change="handleType"
+      ></Select>
+    </div>
+
+    <div class="selct" style="display: inline-block; margin-right: 15px">
+      <RangePicker v-model:value="selectTime"  format="YYYY-MM-DD" @change="handleTime"/>
+    </div>
+
+    <div class="selct" style="display: inline-block">
+      <!-- <span style="margin-right:15px">颗粒度</span> -->
+      <Select
+        v-model:value="value"
+        style="width: 100px; margin-right: 30px"
+        placeholder="请选择颗粒度"
+        :options="options"
+        @change="handleData"
+      ></Select>
+    </div>
+    <a-button type="primary" @click="but">导出</a-button>
+  </div>
+</template>
+<script lang="ts" setup>
+import { ref, Ref, defineEmits } from 'vue';
+const emit = defineEmits(["alertSome"])
+import { Select, DatePicker } from 'ant-design-vue';
+const { RangePicker } = DatePicker;
+// import type { dataItemType } from './props';
+import type { Dayjs } from 'dayjs';
+import dayjs from 'dayjs';
+const props = defineProps({
+  loading: Boolean,
+});
+
+type RangeValue = [Dayjs, Dayjs];
+const picker = ref('date');
+const value = ref('0');
+const selectTime = ref<RangeValue>();
+const options = ref<SelectProps['options']>([
+  {
+    value: '0',
+    label: '日',
+  },
+  {
+    value: '1',
+    label: '周',
+  },
+  {
+    value: '2',
+    label: '月',
+  },
+]);
+const type = ref('0');
+const typeOptions = ref<SelectProps['options']>([
+  {
+    value: '0',
+    label: '数量',
+  },
+  {
+    value: '1',
+    label: '金额',
+  },
+]);
+function handleData(val) {
+  console.log('handleData',val)
+  value.value = val
+  output()
+}
+function handleType(val) {
+  type.value = val
+  output()
+}
+
+function handleTime(val) {
+  console.log('selectTime',val)
+  selectTime.value = val
+  output()
+}
+function but(){
+  emit('expor')
+}
+function output(){
+  let data = {
+    startTime:selectTime.value?.length&&dayjs(selectTime.value[0]).format('YYYY-MM-DD')||'',
+    endTime:selectTime.value?.length&&dayjs(selectTime.value[1]).format('YYYY-MM-DD')||'',
+    dataType:value.value,
+    type:type.value,
+  }
+  emit('change',data)
+}
+</script>

+ 58 - 15
src/views/statistics/components/lineEcharts2.vue

@@ -1,16 +1,30 @@
 <template>
-  <Card title="成交占比" :loading="loading">
+  <Card title="订单数据统计" :loading="loading">
     <template #extra>
       <div class="condition">
           <div class="selct" style="display: inline-block;">
-            <span style="margin-right:15px">颗粒度</span>
+            <!-- <span style="margin-right:15px"></span> -->
+            <Select
+            v-model:value="type"
+            style="width: 100px;margin-right:30px"
+            placeholder="请选择颗粒度"
+            :options="typeOptions"
+            @change="handleType"
+          ></Select>
+          </div>
+          
+          <div class="selct" style="display: inline-block;margin-right:15px">
+            <RangePicker v-model:value="selectTime" :picker="picker" />
+          </div>
+          
+          <div class="selct" style="display: inline-block;">
+            <!-- <span style="margin-right:15px">颗粒度</span> -->
             <Select
             v-model:value="value"
-            label-in-value
             style="width: 100px;margin-right:30px"
             placeholder="请选择颗粒度"
             :options="options"
-            @change="handleChange"
+            @change="handleData"
           ></Select>
           </div>
           <a-button type="primary" >导出</a-button>
@@ -22,25 +36,46 @@
 <script lang="ts">
   import { basicProps } from './props';
   // import { dateUtil } from '/@/utils/dateUtil';
-  import { Card, Select } from 'ant-design-vue';
 </script>
 <script lang="ts" setup>
+  import { Card, Select, DatePicker } from 'ant-design-vue';
+  const {RangePicker} = DatePicker;
   import { ref, Ref, watch } from 'vue';
   // import type { dataItemType } from './props';
   import { useECharts } from '/@/hooks/web/useECharts';
+  import type { Dayjs } from 'dayjs';
   const props = defineProps({
   loading: Boolean,
     ...basicProps,
   });
-  const value = ref('a1');
+  
+  type RangeValue = [Dayjs, Dayjs];
+  const picker = ref('date')
+  const value = ref('1');
+  const selectTime = ref<RangeValue>();
   const options = ref<SelectProps['options']>([
         {
-          value: 'jack',
-          label: 'Jack (100)',
+          value: '1',
+          label: '日',
+        },
+        {
+          value: '2',
+          label: '周',
+        },
+        {
+          value: '2',
+          label: '月',
+        },
+      ]);
+  const type = ref('1');
+  const typeOptions = ref<SelectProps['options']>([
+        {
+          value: '1',
+          label: '数量',
         },
         {
-          value: 'lucy',
-          label: 'Lucy (101)',
+          value: '2',
+          label: '金额',
         },
       ]);
   const viewStaticsData = ref<number[]>([1,5,6,8,55,1,5,6,8,1]);
@@ -49,7 +84,15 @@
   const maxSize = ref(0);
   const chartRef = ref<HTMLDivElement | null>(null);
   const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
-  function handleChange(val){
+  function handleData(val){
+    let obj ={
+      1:'date',
+      2:'week',
+      3:'month',
+    }
+    console.log('handleChange',val)
+  }
+  function handleType(val){
     console.log('handleChange',val)
   }
   function handlesetOptions() {
@@ -81,16 +124,16 @@
       series: [
         {
           data: viewStaticsData.value,
-          type: 'bar',
+          type: 'line',
           itemStyle: { color: '#38a0ff' },
-          barMaxWidth: 80,
+          // barMaxWidth: 80,
           name: '用户浏览量',
         },
         {
           data: shareStaticsData.value,
-          type: 'bar',
+          type: 'line',
           itemStyle: { color: '#4cca73' },
-          barMaxWidth: 80,
+          // barMaxWidth: 80,
           name: '用户分享数',
         },
       ],

+ 111 - 0
src/views/statistics/components/orderEchart.vue

@@ -0,0 +1,111 @@
+<template>
+  <Card :title="title||'订单数据统计'">
+    <template #extra>
+      <condition @change="Search" @expor="expor" />
+    </template>
+    <div ref="chartRef" :style="{ height, width }"></div>
+  </Card>
+</template>
+<script lang="ts" setup>
+  import { basicProps } from './props';
+  import condition from './condition.vue';
+  import { Card, DatePicker } from 'ant-design-vue';
+  import { ref, Ref, watch, defineEmits } from 'vue';
+  import { useECharts } from '/@/hooks/web/useECharts';
+  const props = defineProps({
+  loading: Boolean,
+    ...basicProps,
+  });
+  const emit = defineEmits(["alertSome"])
+  const downOrderData = ref<number[]>([]);
+  const incrementOrderData = ref<number[]>([]);
+  const partsOrderData = ref<number[]>([]);
+  const yixStringData = ref<string[]>([]);
+  const echartTypr = ref('line')
+  const nameList = ref<string[]>(['下载订单','权益订单','配件订单']);
+  const maxSize = ref(0);
+  const chartRef = ref<HTMLDivElement | null>(null);
+  const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>)
+
+  function Search(val){
+    emit('change',val)
+  }
+  function expor(val){
+    emit('expor',val)
+  }
+  function handlesetOptions() {
+    console.log('handlesetOptions',downOrderData.value,partsOrderData.value,partsOrderData.value,yixStringData.value)
+    setOptions({
+      tooltip: {
+        trigger: 'axis',
+        axisPointer: {
+          lineStyle: {
+            width: 1,
+            color: '#019680',
+          },
+        },
+      },
+      legend: {
+        orient: 'horizontal',
+        bottom: 0,
+      },
+      // grid: { left: '2%', right: '2%', top: '10%', bottom: '10%', containLabel: true },
+      xAxis: {
+        type: 'category',
+        // data: [...new Array(30)].map((_item, index) => `${index + 1}日`),
+        data: yixStringData.value,
+      },
+      yAxis: {
+        type: 'value',
+        max: maxSize.value,
+        splitNumber: 4,
+      },
+      series: [
+        {
+          data: downOrderData.value,
+          type: echartTypr.value,
+          itemStyle: { color: '#38a0ff' },
+          barMaxWidth: 80,
+          name: nameList.value[0],
+        },
+        {
+          data: incrementOrderData.value,
+          type: echartTypr.value,
+          itemStyle: { color: '#4cca73' },
+          barMaxWidth: 80,
+          name: nameList.value[1],
+        },
+        {
+          data: partsOrderData.value,
+          type: echartTypr.value,
+          itemStyle: { color: '#FDD56A' },
+          barMaxWidth: 80,
+          name: nameList.value[2],
+        },
+      ],
+    });
+  }
+  watch(
+    () => props.echartData,
+    (echartData) => {
+      downOrderData.value = echartData.downOrder ||[]
+      incrementOrderData.value = echartData.incrementOrder ||[]
+      partsOrderData.value = echartData.partOrder ||[]
+      yixStringData.value = echartData.xdata ||[]
+      if(echartData.nameList){
+        nameList.value = echartData.nameList
+      }
+      if(echartData.echartTypr){
+        echartTypr.value = echartData.echartTypr
+      }
+      const maxNumber = Math.max(...echartData.downOrder.concat(echartData.incrementOrder));
+      const pow = Math.pow(10, maxNumber.toString().length - 1);
+      maxSize.value = maxNumber > 10 ? Math.floor(maxNumber / 10) * 10 + pow * 2 : 10;
+      handlesetOptions();
+    },
+    {
+      immediate: true,
+      deep: true,
+    },
+  );
+</script>

+ 16 - 0
src/views/statistics/components/props.ts

@@ -8,6 +8,14 @@ export type dataItemType = {
   date: string;
   amount: string | number;
 };
+export type echartData = {
+  xdata: string[];
+  downOrder: number[];
+  incrementOrder: number[];
+  partOrder: number[];
+  nameList?:string[];
+  echartTypr?:string;
+};
 export const basicProps = {
   width: {
     type: String as PropType<string>,
@@ -33,4 +41,12 @@ export const basicProps = {
     type: Array as PropType<Array<dataItemType>>,
     default: [],
   },
+  echartData: {
+    type: Object as PropType<echartData>,
+    default: {},
+  },
+  title: {
+    type: String,
+    default: '',
+  },
 };

+ 70 - 20
src/views/statistics/order/index.vue

@@ -1,40 +1,90 @@
 <template>
   <div class="p-4">
-    <!-- <GrowCard :loading="loading" :list="growCardList" class="enter-y" /> -->
-    <!-- <div class="md:flex !my-4 enter-y">
-      <lineEcharts class="md:w-1/2 w-full !md:mt-0 !mt-4 !md:mr-4" name="chartRef1" :loading="loading"  />
-      <lineEcharts2  name="chartRef2" class="md:w-1/2 mx-4 w-full" :loading="loading" />
-    </div> -->
-    <lineEcharts2 class="!my-4 enter-y" :loading="loading" />
-    <!-- <div class="md:flex enter-y">
-      <VisitRadar class="md:w-1/3 w-full" :loading="loading" />
-      <VisitSource class="md:w-1/3 !md:mx-4 !md:my-0 !my-4 w-full" :loading="loading" />
-      <SalesProductPie class="md:w-1/3 w-full" :loading="loading" />
-    </div> -->
+    <GrowCard :loading="loading" :list="growCardList" class="enter-y" /> 
+    <orderEchart title="订单数据统计" class="!my-4 enter-y" @change="Search" :echartData="echartData" :loading="loading" />
   </div>
 </template>
 <script lang="ts" setup>
-  import { ref, onMounted } from 'vue';
-  import { orderTotal } from '/@/api/statistics/index';
+  import { ref, onMounted, reactive } from 'vue';
+  import { orderTotal,orderTrend } from '/@/api/statistics/index';
   import { GrowCardItem } from '../data';
   import GrowCard from '../components/GrowCard.vue';
-  import lineEcharts2 from '../components/lineEcharts2.vue';
+  import orderEchart from '../components/orderEchart.vue';
   const loading = ref(true);
   const growCardList = ref<GrowCardItem[]>([]);
+  const echartData = reactive({
+    xdata:[],
+    downOrder:[],
+    incrementOrder:[],
+    partOrder:[],
+  })
+  const SearchData = reactive({
+    startTime:'',
+    endTime:'',
+    dataType:0,
+    type:0,
+  })
   onMounted(() => {
     getData();
+    getList()
   });
+  async function getList() {
+    let downlist = [],xdata = []
+    loading.value = true;
+    const {downOrder,incrementOrder,partOrder} = await orderTrend(SearchData);
+    downOrder.map(ele => {
+      xdata.push(ele.groupKey)
+      downlist.push(ele.count)
+    })
+    echartData.xdata = xdata
+    echartData.downOrder = downlist
+    echartData.incrementOrder = incrementOrder.map(ele => ele.count)
+    echartData.partOrder = partOrder &&partOrder.map(ele => ele.count)|| []
+    loading.value = false;
+  }
+  function Search(val){
+    const {startTime,endTime,dataType,type} = val
+    console.log('params',val)
+    SearchData.startTime = startTime
+    SearchData.endTime = endTime
+    SearchData.dataType = dataType
+    SearchData.type = type
+    getList()
+  }
   async function getData() {
     try {
       loading.value = true;
-      const Total = await orderTotal();
-      console.log('orderTotal', Total);
-      loading.value = false;
+    const {preMonThPowCount,preMonThDownCount,preMonThPartCount} = await orderTotal();
+    let list:GrowCardItem[] = [
+      {
+        title: '上月权益订单数',
+        icon: 'fxemoji:notchedrightsemi3dot',
+        value: preMonThPowCount,
+        unit: '人',
+        color: 'blue',
+        action: '月',
+      },
+      {
+        title: '上月下载订单数',
+        icon: 'download-count|svg',
+        value: preMonThDownCount,
+        unit: '人',
+        color: 'orange',
+        action: '月',
+      },
+      {
+        title: '上月配件订单数',
+        icon: 'transaction|svg',
+        value: preMonThPartCount,
+        unit: '人',
+        color: 'blue',
+        action: '月',
+      },
+    ];
+    loading.value = false;
+    growCardList.value =list
     } catch (error) {
       loading.value = false;
     }
   }
-  setTimeout(() => {
-    loading.value = false;
-  }, 1500);
 </script>

+ 87 - 18
src/views/statistics/register/index.vue

@@ -1,26 +1,95 @@
 <template>
   <div class="p-4">
     <GrowCard :loading="loading" :list="growCardList" class="enter-y" />
-    <!-- <div class="md:flex !my-4 enter-y">
-      <lineEcharts class="md:w-1/2 w-full !md:mt-0 !mt-4 !md:mr-4" name="chartRef1" :loading="loading"  />
-      <lineEcharts2  name="chartRef2" class="md:w-1/2 mx-4 w-full" :loading="loading" />
-    </div> -->
-    <scenelineEcharts class="!my-4 enter-y" :loading="loading" />
-    <!-- <div class="md:flex enter-y">
-      <VisitRadar class="md:w-1/3 w-full" :loading="loading" />
-      <VisitSource class="md:w-1/3 !md:mx-4 !md:my-0 !my-4 w-full" :loading="loading" />
-      <SalesProductPie class="md:w-1/3 w-full" :loading="loading" />
-    </div> -->
+    <lineEcharts2  name="chartRef2" class="!my-4 enter-y" :loading="loading" />
+    <scenelineEcharts class="!my-4 enter-y" ref="myRefs" :loading="loading" />
   </div>
 </template>
 <script lang="ts" setup>
-  import { ref } from 'vue';
-  import { growCardList } from '../data';
-  import GrowCard from '../components/GrowCard.vue';
-  import scenelineEcharts from '../components/scenelineEcharts.vue';
-  const loading = ref(true);
-
-  setTimeout(() => {
+import { userTotal, sceneTrend } from '/@/api/statistics/index';
+import { ref, onMounted, reactive } from 'vue';
+import { GrowCardItem } from '../data';
+import GrowCard from '../components/GrowCard.vue';
+import scenelineEcharts from '../components/scenelineEcharts.vue';
+const loading = ref(true);
+const growCardList = ref<GrowCardItem[]>([]);
+const echartData = reactive({
+    xdata:[],
+    downOrder:[],
+    incrementOrder:[],
+    partOrder:[],
+  })
+const SearchData = reactive({
+    startTime:'',
+    endTime:'',
+    dataType:0,
+    type:0,
+  })
+onMounted(() => {
+  getData();
+  getList()
+});
+async function getList() {
+    let downlist = [],xdata = []
+    loading.value = true;
+    const {downOrder,incrementOrder,partOrder} = await sceneTrend(SearchData);
+    downOrder.map(ele => {
+      xdata.push(ele.groupKey)
+      downlist.push(ele.count)
+    })
+    echartData.xdata = xdata
+    echartData.downOrder = downlist
+    echartData.incrementOrder = incrementOrder.map(ele => ele.count)
+    echartData.partOrder = partOrder &&partOrder.map(ele => ele.count)|| []
+    loading.value = false;
+  }
+async function getData() {
+  try {
+    loading.value = true;
+    const {totalUserCount,preMonthAddCount,todayAddCount,todayActiveCount} = await userTotal();
+    let list:GrowCardItem[] = [
+      {
+        title: '累计用户',
+        // icon: 'fa6-solid:users-gear',
+        icon: 'visit-count|svg',
+        value: totalUserCount,
+        unit: '人',
+        color: 'green',
+        action: '年',
+      },
+      {
+        title: '上月新增用户',
+        icon: 'akar-icons:person-add',
+        value: preMonthAddCount,
+        unit: '人',
+        color: 'blue',
+        action: '月',
+      },
+      {
+        title: '今日新增用户',
+        icon: 'carbon:user-role',
+        value: todayAddCount,
+        unit: '人',
+        color: 'orange',
+        action: '日',
+      },
+      {
+        title: '今日活跃用户',
+        icon: 'ooui:user-active',
+        value: todayActiveCount,
+        unit: '人',
+        color: 'blue',
+        action: '日',
+      },
+    ];
+    growCardList.value =list
     loading.value = false;
-  }, 1500);
+  } catch (error) {
+    loading.value = false;
+  }
+}
+
+setTimeout(() => {
+  loading.value = false;
+}, 1500);
 </script>

+ 90 - 20
src/views/statistics/scene/index.vue

@@ -1,28 +1,98 @@
 <template>
   <div class="p-4">
     <GrowCard :loading="loading" :list="growCardList" class="enter-y" />
-    <!-- <div class="md:flex !my-4 enter-y">
-      <lineEcharts class="md:w-1/2 w-full !md:mt-0 !mt-4 !md:mr-4" name="chartRef1" :loading="loading"  />
-    </div> -->
-    <lineEcharts2  name="chartRef2" class="!my-4 enter-y" :loading="loading" />
-
-    <scenelineEcharts class="!my-4 enter-y" :loading="loading" />
-    <!-- <div class="md:flex enter-y">
-      <VisitRadar class="md:w-1/3 w-full" :loading="loading" />
-      <VisitSource class="md:w-1/3 !md:mx-4 !md:my-0 !my-4 w-full" :loading="loading" />
-      <SalesProductPie class="md:w-1/3 w-full" :loading="loading" />
-    </div> -->
+    <orderEchart
+      title="订单数据统计"
+      class="!my-4 enter-y"
+      @change="Search"
+      :echartData="echartData"
+      :loading="loading"
+    />
   </div>
 </template>
 <script lang="ts" setup>
-  import { ref } from 'vue';
-  import { growCardList } from '../data';
-  import GrowCard from '../components/GrowCard.vue';
-  import lineEcharts2 from '../components/lineEcharts2.vue';
-  import scenelineEcharts from '../components/scenelineEcharts.vue';
-  const loading = ref(true);
-
-  setTimeout(() => {
+import { ref, onMounted, reactive } from 'vue';
+import { orderTotal, sceneTrend } from '/@/api/statistics/index';
+import { GrowCardItem } from '../data';
+import GrowCard from '../components/GrowCard.vue';
+import orderEchart from '../components/orderEchart.vue';
+const loading = ref(true);
+const growCardList = ref<GrowCardItem[]>([]);
+const echartData = reactive({
+  xdata: [],
+  downOrder: [],
+  incrementOrder: [],
+  partOrder: [],
+});
+const SearchData = reactive({
+  startTime: '',
+  endTime: '',
+  dataType: 0,
+  type: 0,
+});
+onMounted(() => {
+  getData();
+  getList();
+});
+async function getList() {
+  let downlist = [],
+    xdata = [];
+  loading.value = true;
+  const { downOrder, incrementOrder, partOrder } = await sceneTrend(SearchData);
+  downOrder.map((ele) => {
+    xdata.push(ele.groupKey);
+    downlist.push(ele.count);
+  });
+  echartData.xdata = xdata;
+  echartData.downOrder = downlist;
+  echartData.incrementOrder = incrementOrder.map((ele) => ele.count);
+  echartData.partOrder = (partOrder && partOrder.map((ele) => ele.count)) || [];
+  loading.value = false;
+}
+function Search(val) {
+  const { startTime, endTime, dataType, type } = val;
+  console.log('params', val);
+  SearchData.startTime = startTime;
+  SearchData.endTime = endTime;
+  SearchData.dataType = dataType;
+  SearchData.type = type;
+  getList();
+}
+async function getData() {
+  try {
+    loading.value = true;
+    const { preMonThPowCount, preMonThDownCount, preMonThPartCount } = await orderTotal();
+    let list: GrowCardItem[] = [
+      {
+        title: '上月权益订单数',
+        icon: 'fxemoji:notchedrightsemi3dot',
+        value: preMonThPowCount,
+        unit: '人',
+        color: 'blue',
+        action: '月',
+      },
+      {
+        title: '上月下载订单数',
+        icon: 'download-count|svg',
+        value: preMonThDownCount,
+        unit: '人',
+        color: 'orange',
+        action: '月',
+      },
+      {
+        title: '上月配件订单数',
+        icon: 'transaction|svg',
+        value: preMonThPartCount,
+        unit: '人',
+        color: 'blue',
+        action: '月',
+      },
+    ];
     loading.value = false;
-  }, 1500);
+    growCardList.value = list;
+  } catch (error) {
+    loading.value = false;
+  }
+}
 </script>
+