shaogen1995 2 месяцев назад
Родитель
Сommit
d9f4f8ea26

+ 2 - 2
src/components/QRCodeModal/index.tsx

@@ -3,7 +3,7 @@ import { Modal, QRCode, Button, message } from 'antd'
 import { DownloadOutlined } from '@ant-design/icons'
 
 interface QRCodeModalProps {
-  info: { id: number; name: string }
+  info: { id: number; name: string; fId: number }
   onCancel: () => void
 }
 
@@ -68,7 +68,7 @@ const QRCodeModal: React.FC<QRCodeModalProps> = ({ info, onCancel }) => {
         {info.id && (
           <>
             <QRCode
-              value={String(info.id)}
+              value={JSON.stringify({ formDefId: info.fId, formDataId: info.id })}
               size={256}
               bgColor='#ffffff'
               style={{ marginBottom: 16 }}

+ 1 - 15
src/pages/Aanalyse/A1resStat/data.ts

@@ -215,21 +215,7 @@ const generateLibraryData = () => {
     '家谱族谱库',
     '舆图档案库',
     '碑帖拓片库',
-    '契约文书库',
-
-    '1古籍文献库',
-    '1地方志库',
-    '1家谱族谱库',
-    '1舆图档案库',
-    '1碑帖拓片库安师大实打实的',
-    '1契约文书库',
-
-    '2古籍文献库',
-    '2地方志库',
-    '2家谱族谱库',
-    '2舆图档案库',
-    '2碑帖拓片库',
-    '2契约文书库'
+    '契约文书库'
   ]
 
   return categories.map(name => ({

+ 1 - 1
src/pages/Aanalyse/A1resStat/index.tsx

@@ -30,7 +30,7 @@ function A1resStat() {
   }, [])
 
   // 关联字典数组
-  const dictArrTemp = useSelector((state: RootState) => state.Z2dict.dictAll)
+  // const dictArrTemp = useSelector((state: RootState) => state.Z2dict.dictAll)
   // 关联标签
   const biaoQianTemp = useSelector((state: RootState) => state.B3resTag.dictAll)
 

+ 80 - 0
src/pages/Aanalyse/A2visStat/A2look/index.module.scss

@@ -0,0 +1,80 @@
+.A2look {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  z-index: 10;
+  background-color: #f8f8f8;
+  border-radius: 10px;
+  padding: 10px;
+  :global {
+    .A2tit {
+      font-weight: 700;
+      color: var(--txtColor);
+    }
+    .A2time {
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+    }
+
+    .A2Ltop {
+      padding: 0 10px;
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      .A2LT1 {
+        & > h3 {
+          margin-bottom: 5px;
+        }
+      }
+      .A2LT2 {
+        width: calc(100% - 340px);
+        padding-right: 40px;
+        height: 60px;
+        display: flex;
+        justify-content: center;
+        & > div {
+          margin-right: 20px;
+          padding: 5px 0 0 50px;
+          width: 205px;
+          height: 60px;
+          background-color: #fff;
+          border-radius: 10px;
+          & > p {
+            color: var(--txtColor);
+          }
+          & > span {
+            margin-top: 5px;
+            font-size: 22px;
+          }
+        }
+      }
+    }
+
+    .A2Lzhe {
+      margin-top: 10px;
+      padding: 10px;
+      background-color: #fff;
+      border-radius: 10px;
+      #ech3 {
+        height: 260px;
+      }
+    }
+
+    .A2Lzhe2 {
+      height: 450px;
+      .A2time {
+        margin-bottom: 10px;
+        .A2tit {
+          display: flex;
+          align-items: center;
+        }
+      }
+      .ant-table-cell {
+        padding: 8px !important;
+      }
+    }
+  }
+}

+ 233 - 0
src/pages/Aanalyse/A2visStat/A2look/index.tsx

@@ -0,0 +1,233 @@
+import React, { useCallback, useEffect, useMemo, useState } from 'react'
+import styles from './index.module.scss'
+import { Button, DatePicker, Select } from 'antd'
+import { A2getNumFu, A2initXianFu, A2setTimeFu, A2valueAcFu } from '../data'
+import dayjs from 'dayjs'
+import { A2_APIgetUserList, A2_APIgetXianUser } from '@/store/action/Aanalyse/A2visStat'
+import { antdSelectType } from '@/utils/dataChange'
+import { getKuListByOpenFu } from '@/pages/Zother/data'
+import { ziyuanAcGet, ziyuanAcSet } from '@/utils/storage'
+import MyTable from '@/components/MyTable'
+import { openLink } from '@/utils/history'
+import { A2sonTableC } from '@/utils/tableData'
+const { RangePicker } = DatePicker
+const timeArr = [14, 30]
+
+type Props = {
+  info: any
+  closeFu: () => void
+}
+
+function A2look({ info, closeFu }: Props) {
+  // 柱状图
+  const [timeValue1, setTimeValue1] = useState<string[]>([])
+
+  // 列表
+
+  //   const [time2Obj,setTime2Obj] =useState< {
+  //     formDefId: number;
+  //     time: string[];
+  // }>({formDefId:0,time:[]})
+
+  const [timeValue2, setTimeValue2] = useState<string[]>([])
+  const [formDefId, setFormDefId] = useState(0)
+
+  const [kuList, setKuList] = useState<antdSelectType[]>([])
+  const [loding, setLoding] = useState(false)
+  useEffect(() => {
+    getKuListByOpenFu(data => {
+      setLoding(true)
+      if (data && data.length) {
+        setKuList(data)
+        const ziyuanAcTxt = ziyuanAcGet()
+        let objRes = data.find(v => v.value === ziyuanAcTxt)
+
+        const resFid = objRes ? ziyuanAcTxt : data[0].value
+        setFormDefId(resFid)
+      }
+    })
+  }, [])
+
+  useEffect(() => {
+    A2setTimeFu(14, setTimeValue1)
+    A2setTimeFu(14, setTimeValue2)
+  }, [])
+
+  useEffect(() => {
+    A2getNumFu(timeValue1, A2_APIgetXianUser, '#ech3', A2initXianFu, { loginUserId: info.userId })
+  }, [info.userId, timeValue1])
+
+  // 获取列表
+
+  const [list, setList] = useState<any[]>([])
+
+  const getListFu = useCallback(async () => {
+    const obj = {
+      startTime: timeValue2[0] + ' 00:00:00',
+      endTime: timeValue2[1] + ' 23:59:59',
+      formDefId,
+      loginUserId: info.userId
+    }
+
+    if (obj.formDefId) {
+      const res = await A2_APIgetUserList(obj)
+      if (res.code === 0) {
+        setList(res.data || [])
+      }
+    }
+  }, [formDefId, info.userId, timeValue2])
+
+  useEffect(() => {
+    getListFu()
+  }, [getListFu])
+
+  const tableLastBtn = useMemo(() => {
+    return [
+      {
+        title: '操作',
+        render: (item: any) => (
+          <>
+            <Button
+              size='small'
+              type='text'
+              onClick={() => openLink(`/goodsLook/${item.form_data_id}/${formDefId}`)}
+            >
+              查看藏品
+            </Button>
+          </>
+        )
+      }
+    ]
+  }, [formDefId])
+
+  return (
+    <div className={styles.A2look}>
+      <div className='A2Ltop'>
+        <div className='A2LT1'>
+          <h3>{info.creatorName}</h3>
+          <p>
+            账号名称:{info.userName}&emsp;|&emsp;{info.roleName}
+          </p>
+        </div>
+        <div className='A2LT2'>
+          <div>
+            <p>总计访问次数</p>
+            <span>{info.pcs_visit}</span>
+          </div>
+          <div>
+            <p>阅读文献总数</p>
+            <span>{info.pcs_read}</span>
+          </div>
+          <div>
+            <p>总阅读时长(分)</p>
+            <span>{info.pcs_time}</span>
+          </div>
+        </div>
+        <Button onClick={closeFu}>返回</Button>
+      </div>
+
+      <div className='A2Lzhe'>
+        <div className='A2time'>
+          <div className='A2tit'>访问趋势</div>
+          <div className='A2timeRR'>
+            {timeArr.map(v => (
+              <Button
+                type={A2valueAcFu(timeValue1) === v ? 'primary' : 'default'}
+                onClick={() => A2setTimeFu(v, setTimeValue1)}
+                size='small'
+                key={v}
+              >
+                最近{v}天
+              </Button>
+            ))}
+            &emsp;
+            <RangePicker
+              allowClear={false}
+              value={
+                timeValue1 && timeValue1.length
+                  ? [dayjs(timeValue1[0]), dayjs(timeValue1[1])]
+                  : undefined
+              }
+              onChange={(_, e) => setTimeValue1(e)}
+              size='small'
+              disabledDate={current => {
+                const minDate = dayjs().subtract(90, 'day').startOf('day')
+                const maxDate = dayjs().subtract(1, 'day').endOf('day')
+                return current < minDate || current > maxDate
+              }}
+            />
+          </div>
+        </div>
+        <div id='ech3'></div>
+      </div>
+
+      <div className='A2Lzhe A2Lzhe2'>
+        <div className='A2time'>
+          <div className='A2tit'>
+            访问明细 &emsp;
+            {formDefId ? (
+              <div className='Z0ku'>
+                资源库:
+                <Select
+                  style={{ maxWidth: 200, minWidth: 120 }}
+                  value={formDefId}
+                  onChange={e => {
+                    setFormDefId(e)
+                    ziyuanAcSet(e)
+                  }}
+                  options={kuList}
+                />
+              </div>
+            ) : null}
+          </div>
+          <div className='A2timeRR'>
+            {timeArr.map(v => (
+              <Button
+                type={A2valueAcFu(timeValue2) === v ? 'primary' : 'default'}
+                onClick={() => A2setTimeFu(v, setTimeValue2)}
+                size='small'
+                key={v}
+              >
+                最近{v}天
+              </Button>
+            ))}
+            &emsp;
+            <RangePicker
+              allowClear={false}
+              value={
+                timeValue2 && timeValue2.length
+                  ? [dayjs(timeValue2[0]), dayjs(timeValue2[1])]
+                  : undefined
+              }
+              onChange={(_, e) => setTimeValue2(e)}
+              size='small'
+              disabledDate={current => {
+                const minDate = dayjs().subtract(90, 'day').startOf('day')
+                const maxDate = dayjs().subtract(1, 'day').endOf('day')
+                return current < minDate || current > maxDate
+              }}
+            />
+          </div>
+        </div>
+        {formDefId ? (
+          <MyTable
+            classKey='A2look'
+            yHeight={355}
+            list={list}
+            columnsTemp={A2sonTableC}
+            lastBtn={tableLastBtn}
+            pagingInfo={false}
+          />
+        ) : (
+          <div hidden={!loding} className='Z0null'>
+            请先在 系统管理-资源库管理 添加数据
+          </div>
+        )}
+      </div>
+    </div>
+  )
+}
+
+const MemoA2look = React.memo(A2look)
+
+export default MemoA2look

+ 13 - 0
src/pages/Aanalyse/A2visStat/A2table/index.module.scss

@@ -0,0 +1,13 @@
+.A2table {
+  padding: 10px;
+  :global {
+    .A2Ttop {
+      display: flex;
+      margin-bottom: 15px;
+      justify-content: space-between;
+      & > div {
+        display: flex;
+      }
+    }
+  }
+}

+ 144 - 0
src/pages/Aanalyse/A2visStat/A2table/index.tsx

@@ -0,0 +1,144 @@
+import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
+import styles from './index.module.scss'
+import { useDispatch, useSelector } from 'react-redux'
+import { RootState } from '@/store'
+import { Z6_APIgetList } from '@/store/action/Zsystem/Z6roleSet'
+import { A2_APIgetList } from '@/store/action/Aanalyse/A2visStat'
+import { Button, Input, Select } from 'antd'
+import MyTable from '@/components/MyTable'
+import { A2TableC } from '@/utils/tableData'
+import { backPageFu } from '@/utils/history'
+import A2look from '../A2look'
+
+const baseFrom = {
+  pageNum: 1,
+  pageSize: 10,
+  searchKey: '',
+  roleId: null
+}
+function A2table() {
+  const dispatch = useDispatch()
+
+  useEffect(() => {
+    dispatch(Z6_APIgetList({ pageNum: 1, pageSize: 99999 }))
+  }, [dispatch])
+
+  // 获取角色列表
+  const roleList = useSelector((state: RootState) => state.Z6roleSet.tableInfo.list)
+
+  // 顶部筛选
+  const [formData, setFormData] = useState({ ...baseFrom })
+  const formDataRef = useRef({ ...baseFrom })
+
+  useEffect(() => {
+    formDataRef.current = { ...formData }
+  }, [formData])
+
+  // 输入框和下拉框变化
+  const changeDataFu = useCallback(
+    (val: any, key: 'roleId' | 'searchKey') => {
+      setFormData({ ...formData, [key]: val })
+    },
+    [formData]
+  )
+
+  const [tableInfo, setTableInfo] = useState<any>({ list: [], total: 0 })
+
+  const getListFu = useCallback(async () => {
+    const res = await A2_APIgetList(formData)
+    if (res.code === 0) {
+      setTableInfo({ list: res.data.records || [], total: res.data.total })
+    }
+  }, [formData])
+
+  // 点击搜索的 时间戳
+  const [timeKey, setTimeKey] = useState(0)
+
+  // 点击搜索
+  const clickSearch = useCallback(() => {
+    setFormData({ ...formData, pageNum: 1 })
+    setTimeout(() => {
+      setTimeKey(Date.now())
+    }, 50)
+  }, [formData])
+
+  // 点击重置
+  const resetSelectFu = useCallback(() => {
+    setFormData({ ...baseFrom })
+    setTimeout(() => {
+      setTimeKey(Date.now())
+    }, 50)
+  }, [])
+
+  useEffect(() => {
+    getListFu()
+  }, [getListFu, timeKey])
+
+  // 表格里面点击查看
+  const [lookInfo, setLookInfo] = useState<any>({})
+
+  const tableLastBtn = useMemo(() => {
+    return [
+      {
+        title: '操作',
+        render: (item: any) => (
+          <>
+            <Button size='small' type='text' onClick={() => setLookInfo(item)}>
+              查看
+            </Button>
+          </>
+        )
+      }
+    ]
+  }, [])
+
+  return (
+    <div className={styles.A2table}>
+      <div className='pageTitle'>访问统计-用户访问统计</div>
+
+      <div className='A2Ttop'>
+        <div>
+          <Input
+            style={{ width: 260 }}
+            placeholder='请输入用户名'
+            value={formData.searchKey}
+            onChange={e => changeDataFu(e.target.value.trim(), 'searchKey')}
+          />
+          &emsp;
+          <Select
+            style={{ width: 180 }}
+            placeholder='角色'
+            options={roleList.map(v => ({ value: v.id, label: v.roleName }))}
+            value={formData.roleId}
+            onChange={e => changeDataFu(e, 'roleId')}
+          />
+          &emsp;
+          <Button type='primary' onClick={clickSearch}>
+            查询
+          </Button>
+          &emsp;
+          <Button onClick={() => resetSelectFu()}>重置</Button>
+        </div>
+        <Button onClick={() => backPageFu('visStat')}>返回</Button>
+      </div>
+
+      <MyTable
+        yHeight={670}
+        list={tableInfo.list}
+        columnsTemp={A2TableC}
+        lastBtn={tableLastBtn}
+        pageNum={formData.pageNum}
+        pageSize={formData.pageSize}
+        total={tableInfo.total}
+        onChange={(pageNum, pageSize) => setFormData({ ...formData, pageNum, pageSize })}
+      />
+
+      {/* 查看 */}
+      {lookInfo.id ? <A2look info={lookInfo} closeFu={() => setLookInfo({})} /> : null}
+    </div>
+  )
+}
+
+const MemoA2table = React.memo(A2table)
+
+export default MemoA2table

+ 271 - 0
src/pages/Aanalyse/A2visStat/data.ts

@@ -0,0 +1,271 @@
+import dayjs from 'dayjs'
+import * as echarts from 'echarts'
+
+// 改变时间选择器格式
+export const A2setTimeFu = (time: number, fu: any) => {
+  const nowTime = Date.now()
+  const tt1 = nowTime - time * 1000 * 60 * 60 * 24
+  const tt2 = nowTime - 1000 * 60 * 60 * 24
+  const arr = [dayjs(tt1).format('YYYY-MM-DD'), dayjs(tt2).format('YYYY-MM-DD')]
+  fu(arr)
+}
+
+// 最近几天按钮高亮
+export const A2valueAcFu = (val: string[] | undefined) => {
+  let num = 0
+  if (val && val.length) {
+    const startDate = dayjs(val[0])
+    const endDate = dayjs(val[1])
+    num = endDate.diff(startDate, 'day') + 1
+  }
+  return num
+}
+
+// 发送接口
+export const A2getNumFu = async (value: any, API: any, dom: string, fu: any, moreObj?: any) => {
+  if (value && value.length && value.length === 2) {
+    const obj = {
+      startTime: value[0] + ' 00:00:00',
+      endTime: value[1] + ' 23:59:59',
+      ...(moreObj || {})
+    }
+
+    const res = await API(obj)
+    if (res.code === 0) {
+      fu(res.data || [], document.querySelector(dom))
+    }
+  }
+}
+
+type ZhuType = {
+  pcs_visit: number
+  roleName: string
+  id: number
+}
+
+// 根据传进来的data生成柱状图
+export const A2initZhuFu = (data: ZhuType[], dom: any) => {
+  if (!dom) return
+
+  // 销毁已存在的实例
+  const existingChart = echarts.getInstanceByDom(dom)
+  if (existingChart) {
+    existingChart.dispose()
+  }
+
+  // 创建图表实例
+  const myChart = echarts.init(dom)
+
+  // 提取数据和角色名称
+  const roleNames = data.map(item => item.roleName)
+  const visitCounts = data.map(item => item.pcs_visit)
+
+  // 柱状图配置
+  const option = {
+    tooltip: {
+      trigger: 'axis',
+      axisPointer: {
+        type: 'shadow'
+      }
+    },
+    grid: {
+      left: '3%',
+      right: '4%',
+      bottom: '3%',
+      containLabel: true
+    },
+    xAxis: {
+      type: 'category',
+      data: roleNames,
+      axisLabel: {
+        interval: 0,
+        rotate: roleNames.length > 5 ? 30 : 0
+      }
+    },
+    yAxis: {
+      type: 'value',
+      name: '访问次数'
+    },
+    series: [
+      {
+        name: '访问次数',
+        type: 'bar',
+        data: visitCounts,
+        barWidth: '50%',
+        itemStyle: {
+          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+            { offset: 0, color: '#83bff6' },
+            { offset: 0.5, color: '#188df0' },
+            { offset: 1, color: '#188df0' }
+          ])
+        },
+        // 在柱状图顶部显示数值
+        label: {
+          show: true,
+          position: 'top',
+          formatter: '{c}',
+          fontSize: 14,
+          color: '#333'
+        }
+      }
+    ]
+  }
+
+  // 设置配置项
+  myChart.setOption(option)
+
+  // 响应式
+  window.addEventListener('resize', () => {
+    myChart.resize()
+  })
+
+  return myChart
+}
+
+// 根据传进来的data生成折线图
+export const A2initXianFu = (
+  data: {
+    pcsVisit: number
+    groupKey: string
+  }[],
+  dom: any
+) => {
+  if (!dom) return
+
+  // 销毁已存在的实例
+  const existingChart = echarts.getInstanceByDom(dom)
+  if (existingChart) {
+    existingChart.dispose()
+  }
+
+  // 创建图表实例
+  const myChart = echarts.init(dom)
+
+  // 提取数据
+  const dates = data.map(item => item.groupKey)
+  const visitCounts = data.map(item => item.pcsVisit)
+
+  // 根据数据量决定是否显示滚动条
+  const showDataZoom = data.length > 15
+
+  // 折线图配置
+  const option = {
+    tooltip: {
+      trigger: 'axis',
+      axisPointer: {
+        type: 'line'
+      }
+    },
+    grid: {
+      left: '3%',
+      right: '4%',
+      bottom: showDataZoom ? '15%' : '3%',
+      containLabel: true
+    },
+    xAxis: {
+      type: 'category',
+      data: dates,
+      boundaryGap: false,
+      axisLabel: {
+        interval: 0,
+        rotate: dates.length > 10 ? 30 : 0,
+        formatter: (value: string) => {
+          // 如果是日期格式,可以格式化显示
+          return value
+        }
+      }
+    },
+    yAxis: {
+      type: 'value',
+      name: '访问次数',
+      splitLine: {
+        lineStyle: {
+          type: 'dashed'
+        }
+      }
+    },
+    // 数据太多时显示滚动条
+    dataZoom: showDataZoom
+      ? [
+          {
+            type: 'slider',
+            show: true,
+            xAxisIndex: [0],
+            start: Math.max(0, 100 - (15 / data.length) * 100), // 默认显示最后15条数据
+            end: 100,
+            height: 20,
+            bottom: 10,
+            borderColor: 'transparent',
+            backgroundColor: '#e2e2e2',
+            handleIcon:
+              'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z',
+            handleSize: '80%',
+            handleStyle: {
+              color: '#fff',
+              shadowBlur: 3,
+              shadowColor: 'rgba(0, 0, 0, 0.6)',
+              shadowOffsetX: 2,
+              shadowOffsetY: 2
+            },
+            textStyle: {
+              color: '#333'
+            }
+          },
+          {
+            type: 'inside',
+            xAxisIndex: [0],
+            start: Math.max(0, 100 - (15 / data.length) * 100),
+            end: 100,
+            zoomOnMouseWheel: true,
+            moveOnMouseMove: true
+          }
+        ]
+      : [],
+    series: [
+      {
+        name: '访问次数',
+        type: 'line',
+        data: visitCounts,
+        smooth: true,
+        symbol: 'circle',
+        symbolSize: 8,
+        lineStyle: {
+          width: 3,
+          color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
+            { offset: 0, color: '#188df0' },
+            { offset: 1, color: '#83bff6' }
+          ])
+        },
+        itemStyle: {
+          color: '#188df0',
+          borderColor: '#fff',
+          borderWidth: 2
+        },
+        areaStyle: {
+          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+            { offset: 0, color: 'rgba(24, 141, 240, 0.4)' },
+            { offset: 1, color: 'rgba(24, 141, 240, 0.05)' }
+          ])
+        },
+        emphasis: {
+          focus: 'series',
+          itemStyle: {
+            color: '#188df0',
+            borderColor: '#fff',
+            borderWidth: 3
+          }
+        }
+      }
+    ]
+  }
+
+  // 设置配置项
+  myChart.setOption(option)
+
+  // 响应式
+  window.addEventListener('resize', () => {
+    myChart.resize()
+  })
+
+  return myChart
+}

+ 87 - 0
src/pages/Aanalyse/A2visStat/index.module.scss

@@ -1,4 +1,91 @@
 .A2visStat {
+  padding: 15px;
+  font-size: 16px;
+  position: relative;
   :global {
+    .A2tit {
+      font-weight: 700;
+      color: var(--txtColor);
+    }
+    .A2time {
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+    }
+
+    .A2box1 {
+      display: flex;
+
+      .A2box1ll {
+        width: calc(50% - 10px);
+        margin-right: 20px;
+        .A2txt {
+          margin-bottom: 15px;
+          display: flex;
+          justify-content: space-around;
+          .A2txtRow {
+            padding: 20px 0 0 50px;
+            width: 205px;
+            height: 100px;
+            background-color: #fff;
+            border-radius: 10px;
+            & > p {
+              color: var(--txtColor);
+            }
+            & > div {
+              margin-top: 10px;
+              font-size: 24px;
+            }
+          }
+        }
+        .echBox1 {
+          background-color: #fff;
+          border-radius: 10px;
+          padding: 10px;
+
+          #ech1 {
+            width: 100%;
+            height: 300px;
+          }
+        }
+      }
+
+      .A2box1rr {
+        width: calc(50% - 10px);
+        background-color: #fff;
+        border-radius: 10px;
+        .A2rTop {
+          padding: 10px;
+          display: flex;
+          justify-content: space-between;
+          & > p {
+            cursor: pointer;
+            text-decoration: underline;
+          }
+        }
+        .ant-table-cell {
+          padding: 8px !important;
+        }
+        .ant-table-body {
+          background-color: #fff !important;
+        }
+      }
+    }
+
+    .A2box2 {
+      padding: 10px;
+      margin-top: 15px;
+      width: 100%;
+      height: 335px;
+      background-color: #fff;
+      border-radius: 10px;
+      .A2time {
+        width: calc(50% - 20px);
+      }
+      #ech2 {
+        width: 100%;
+        height: calc(100% - 20px);
+      }
+    }
   }
 }

+ 190 - 2
src/pages/Aanalyse/A2visStat/index.tsx

@@ -1,9 +1,197 @@
-import React from 'react'
+import React, { useCallback, useEffect, useMemo, useState } from 'react'
 import styles from './index.module.scss'
+import { Button, DatePicker } from 'antd'
+import dayjs from 'dayjs'
+import {
+  A2_APIgetAllNum,
+  A2_APIgetList,
+  A2_APIgetRoleNum,
+  A2_APIgetXian
+} from '@/store/action/Aanalyse/A2visStat'
+import { A2getNumFu, A2initXianFu, A2initZhuFu, A2setTimeFu, A2valueAcFu } from './data'
+import MyTable from '@/components/MyTable'
+import { A2TableC } from '@/utils/tableData'
+import A2look from './A2look'
+import history from '@/utils/history'
+const { RangePicker } = DatePicker
+
+const timeArr = [7, 14, 30]
+
 function A2visStat() {
+  // 获取-总访问量
+  const [allNum, setAllNum] = useState<any>({})
+
+  const getAllNum = useCallback(async () => {
+    const res = await A2_APIgetAllNum()
+    if (res.code === 0) {
+      setAllNum(res.data || {})
+    }
+  }, [])
+
+  // 柱状图和折线图
+  const [timeValue1, setTimeValue1] = useState<string[]>([])
+  const [timeValue2, setTimeValue2] = useState<string[]>([])
+
+  useEffect(() => {
+    A2setTimeFu(7, setTimeValue1)
+    A2setTimeFu(7, setTimeValue2)
+  }, [])
+
+  useEffect(() => {
+    A2getNumFu(timeValue1, A2_APIgetRoleNum, '#ech1', A2initZhuFu)
+  }, [timeValue1])
+
+  useEffect(() => {
+    A2getNumFu(timeValue2, A2_APIgetXian, '#ech2', A2initXianFu)
+  }, [timeValue2])
+
+  // 用户访问统计
+  const [list, setList] = useState<any[]>([])
+
+  const getListFu = useCallback(async () => {
+    const res = await A2_APIgetList({ pageNum: 1, pageSize: 10 })
+    if (res.code === 0) {
+      setList(res.data.records || [])
+    }
+  }, [])
+
+  useEffect(() => {
+    getAllNum()
+    getListFu()
+  }, [getAllNum, getListFu])
+
+  // 表格里面点击查看
+  const [lookInfo, setLookInfo] = useState<any>({})
+
+  const tableLastBtn = useMemo(() => {
+    return [
+      {
+        title: '操作',
+        render: (item: any) => (
+          <>
+            <Button size='small' type='text' onClick={() => setLookInfo(item)}>
+              查看
+            </Button>
+          </>
+        )
+      }
+    ]
+  }, [])
+
   return (
     <div className={styles.A2visStat}>
-      <h1>A2visStat</h1>
+      <div className='pageTitle'>访问统计{lookInfo.id ? '-' + lookInfo.creatorName : ''}</div>
+
+      <div className='A2box1'>
+        <div className='A2box1ll'>
+          <div className='A2txt'>
+            <div className='A2txtRow'>
+              <p>总计访问次数</p>
+              <div>{allNum.pcs_visit || 0}</div>
+            </div>
+
+            <div className='A2txtRow'>
+              <p>阅读资源总数</p>
+              <div>{allNum.pcs_read || 0}</div>
+            </div>
+
+            <div className='A2txtRow'>
+              <p>平均阅读时长(分)</p>
+              <div>{allNum.pcs_time || 0}</div>
+            </div>
+          </div>
+          <div className='echBox1'>
+            <div className='A2time'>
+              <div className='A2tit'>各角色访问次数</div>
+              <div className='A2timeRR'>
+                {timeArr.map(v => (
+                  <Button
+                    type={A2valueAcFu(timeValue1) === v ? 'primary' : 'default'}
+                    onClick={() => A2setTimeFu(v, setTimeValue1)}
+                    size='small'
+                    key={v}
+                  >
+                    最近{v}天
+                  </Button>
+                ))}
+                &emsp;
+                <RangePicker
+                  allowClear={false}
+                  value={
+                    timeValue1 && timeValue1.length
+                      ? [dayjs(timeValue1[0]), dayjs(timeValue1[1])]
+                      : undefined
+                  }
+                  onChange={(_, e) => setTimeValue1(e)}
+                  size='small'
+                  disabledDate={current => {
+                    const minDate = dayjs().subtract(90, 'day').startOf('day')
+                    const maxDate = dayjs().subtract(1, 'day').endOf('day')
+                    return current < minDate || current > maxDate
+                  }}
+                />
+              </div>
+            </div>
+            <div id='ech1'></div>
+          </div>
+        </div>
+
+        <div className='A2box1rr'>
+          <div className='A2rTop'>
+            <div className='A2tit'>用户访问统计</div>
+            <p onClick={() => history.push(`/visStat_edit`)}>查看更多 &gt;</p>
+          </div>
+
+          {/* 表格 */}
+          <MyTable
+            classKey='A2visStat'
+            yHeight={370}
+            list={list}
+            columnsTemp={A2TableC}
+            lastBtn={tableLastBtn}
+            pagingInfo={false}
+          />
+        </div>
+      </div>
+
+      {/* 折线图 */}
+      <div className='A2box2'>
+        <div className='A2time'>
+          <div className='A2tit'>总访问趋势</div>
+          <div className='A2timeRR'>
+            {timeArr.map(v => (
+              <Button
+                type={A2valueAcFu(timeValue2) === v ? 'primary' : 'default'}
+                onClick={() => A2setTimeFu(v, setTimeValue2)}
+                size='small'
+                key={v}
+              >
+                最近{v}天
+              </Button>
+            ))}
+            &emsp;
+            <RangePicker
+              allowClear={false}
+              value={
+                timeValue2 && timeValue2.length
+                  ? [dayjs(timeValue2[0]), dayjs(timeValue2[1])]
+                  : undefined
+              }
+              onChange={(_, e) => setTimeValue2(e)}
+              size='small'
+              disabledDate={current => {
+                const minDate = dayjs().subtract(90, 'day').startOf('day')
+                const maxDate = dayjs().subtract(1, 'day').endOf('day')
+                return current < minDate || current > maxDate
+              }}
+            />
+          </div>
+        </div>
+        <div id='ech2'></div>
+      </div>
+
+      {/* 查看 */}
+      {lookInfo.id ? <A2look info={lookInfo} closeFu={() => setLookInfo({})} /> : null}
     </div>
   )
 }

+ 3 - 3
src/pages/Bresource/B1overview/index.tsx

@@ -24,7 +24,7 @@ function B1overview() {
   const [loding, setLoding] = useState(false)
 
   // 二维码Modal状态
-  const [qrCodeId, setQrCodeId] = useState({ id: 0, name: '' })
+  const [qrCodeId, setQrCodeId] = useState({ id: 0, name: '', fId: 0 })
 
   const dispatch = useDispatch()
 
@@ -155,7 +155,7 @@ function B1overview() {
         }
       } else {
         // 根据id生成二维码
-        setQrCodeId({ id, name: name! })
+        setQrCodeId({ id, name: name!, fId: formData.formDefId })
       }
     },
     [formData.formDefId, getList]
@@ -397,7 +397,7 @@ function B1overview() {
 
       {/* 二维码Modal */}
       {qrCodeId.id ? (
-        <QRCodeModal info={qrCodeId} onCancel={() => setQrCodeId({ id: 0, name: '' })} />
+        <QRCodeModal info={qrCodeId} onCancel={() => setQrCodeId({ id: 0, name: '', fId: 0 })} />
       ) : null}
     </div>
   )

+ 6 - 0
src/pages/Layout/data.ts

@@ -198,6 +198,12 @@ export default tabLeftArr
 // 这里的id要和tab栏的id相同,不然没有权限进入页面
 export const routerSon: RouterTypeRow[] = [
   {
+    id: 120,
+    name: '访问统计-详情页',
+    path: '/visStat_edit',
+    Com: React.lazy(() => import('../Aanalyse/A2visStat/A2table'))
+  },
+  {
     id: 310,
     name: '资源入库-详情页',
     path: '/resIn_edit/:key/:id/:fId',

+ 32 - 0
src/store/action/Aanalyse/A2visStat.ts

@@ -0,0 +1,32 @@
+import http from '@/utils/http'
+
+// 访问统计--总-各角色访问次数
+export const A2_APIgetRoleNum = (data: any) => {
+  return http.post(`cms/countVisit/allVisitRole`, data)
+}
+
+// 访问统计--总-访问量
+export const A2_APIgetAllNum = () => {
+  return http.get('cms/countVisit/allTotal')
+}
+
+// 访问统计--总-访问趋势
+export const A2_APIgetXian = (data: any) => {
+  return http.post('cms/countVisit/allTrend', data)
+}
+
+// 访问统计--总-用户访问统计
+export const A2_APIgetList = (data: any) => {
+  return http.post('cms/countVisit/userPage', data)
+}
+
+// --------------------详情
+// 访问统计--用户-访问趋势
+export const A2_APIgetXianUser = (data: any) => {
+  return http.post('cms/countVisit/userTrend', data)
+}
+
+// 访问统计--用户-访问明细
+export const A2_APIgetUserList = (data: any) => {
+  return http.post('cms/countVisit/userVisitInfo', data)
+}

+ 16 - 0
src/utils/tableData.ts

@@ -16,6 +16,22 @@
 
 import { selectObj } from './dataChange'
 
+export const A2TableC = [
+  ['txt', '用户名', 'creatorName'],
+  ['txt', '角色', 'roleName'],
+  ['txt', '总访问次数', 'pcs_visit'],
+  ['txt', '阅读文献总数', 'pcs_read'],
+  ['txt', '总阅读时长(分)', 'pcs_time']
+]
+
+export const A2sonTableC = [
+  ['txt', '资源名称', 'name'],
+  ['txt', '责任者', 'create_by'],
+  ['txt', '级别', 'level_perm'],
+  ['dateRes', '访问日期', 'today'],
+  ['txt', '访问次数', 'pcs_visit']
+]
+
 export const I3tableC = [
   ['txt', '编号类型', 'name'],
   ['txt', '前缀', 'prefix'],