jwyu 9 ماه پیش
والد
کامیت
3d48fc345d

+ 8 - 4
src/api/customer/modules/statistic.js

@@ -9,9 +9,13 @@ export default {
   readRecordInfo: params => {
     return get("/read/detail", params);
   },
-  // 用户阅读统计图数据
-  readRecordChartData:params=>{
-    return get('/read/chart/info',params)
-  }
+  // 用户阅读量统计图数据
+  readTimesChartData:params=>{
+    return get('/read/readCntChart',params)
+  },
+  // 用户阅读品种分布图数据
+  readPermissionChartData:params=>{
+    return get('/read/readPermissionChart',params)
+  },
   
 };

+ 4 - 0
src/api/customer/modules/user.js

@@ -30,6 +30,10 @@ export default {
   //删除用户
   userDelete:params=>{
     return post('/user/delete',params)
+  },
+  // 报告分类
+  reportClassify:()=>{
+    return get('/classify/list',{})
   }
   
 };

+ 4 - 0
src/assets/svg/menu/setting.svg

@@ -0,0 +1,4 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M11.0002 8C11.0002 9.65685 9.65702 11 8.00017 11C6.34331 11 5.00017 9.65685 5.00017 8C5.00017 6.34315 6.34331 5 8.00017 5C9.65702 5 11.0002 6.34315 11.0002 8ZM10.0002 8C10.0002 6.89543 9.10474 6 8.00017 6C6.8956 6 6.00017 6.89543 6.00017 8C6.00017 9.10457 6.8956 10 8.00017 10C9.10474 10 10.0002 9.10457 10.0002 8Z" fill="white"/>
+<path d="M8.00017 1.25L14.0623 4.625V11.375L8.00017 14.75L1.93799 11.375V4.625L8.00017 1.25ZM2.93799 5.2128V10.7872L8.00017 13.6055L13.0623 10.7872V5.2128L8.00017 2.39453L2.93799 5.2128Z" fill="white"/>
+</svg>

+ 4 - 0
src/assets/svg/menu/user.svg

@@ -0,0 +1,4 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M11.5 5C11.5 6.933 9.933 8.5 8 8.5C6.067 8.5 4.5 6.933 4.5 5C4.5 3.067 6.067 1.5 8 1.5C9.933 1.5 11.5 3.067 11.5 5ZM10.5 5C10.5 3.61929 9.38071 2.5 8 2.5C6.61929 2.5 5.5 3.61929 5.5 5C5.5 6.38071 6.61929 7.5 8 7.5C9.38071 7.5 10.5 6.38071 10.5 5Z" fill="white"/>
+<path d="M13.9631 10.8528C14.297 11.0122 14.5 11.3547 14.5 11.7246V14C14.5 14.2761 14.2761 14.5 14 14.5H2C1.72386 14.5 1.5 14.2761 1.5 14V11.7246C1.5 11.3547 1.70302 11.0122 2.03686 10.8528C3.8494 9.98708 5.86651 9.5 8 9.5C10.1335 9.5 12.1506 9.98708 13.9631 10.8528ZM8 10.5C6.0334 10.5 4.17435 10.9457 2.5 11.7398V13.5H13.5V11.7398C11.8257 10.9457 9.9666 10.5 8 10.5Z" fill="white"/>
+</svg>

+ 6 - 0
src/assets/svg/menu/usergroup.svg

@@ -0,0 +1,6 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M6.00003 1C6.52575 1 7.02232 1.12482 7.46171 1.34645L6.83144 2.1586C6.57424 2.05626 6.2937 2 6.00003 2C4.75739 2 3.75003 3.00736 3.75003 4.25C3.75003 5.47717 4.73246 6.47488 5.95373 6.49953V7.49968C4.18015 7.47491 2.75003 6.02947 2.75003 4.25C2.75003 2.45507 4.2051 1 6.00003 1Z" fill="white"/>
+<path d="M0.540929 9.43593C2.18917 8.66419 4.01922 8.22779 5.95373 8.22094V9.22095C4.18421 9.22774 2.51071 9.62379 1 10.3252V12H2.85709V13H0.5C0.223857 13 0 12.7761 0 12.5V10.3086C0 9.9372 0.204537 9.59344 0.540929 9.43593Z" fill="white"/>
+<path d="M9.99992 8.31354C8.20499 8.31354 6.74992 6.85846 6.74992 5.06354C6.74992 3.26861 8.20499 1.81354 9.99992 1.81354C11.7948 1.81354 13.2499 3.26861 13.2499 5.06354C13.2499 6.85846 11.7948 8.31354 9.99992 8.31354ZM9.99992 7.31354C11.2426 7.31354 12.2499 6.30618 12.2499 5.06354C12.2499 3.8209 11.2426 2.81354 9.99992 2.81354C8.75728 2.81354 7.74992 3.8209 7.74992 5.06354C7.74992 6.30618 8.75728 7.31354 9.99992 7.31354Z" fill="white"/>
+<path d="M16 11.5178C16 11.1464 15.7954 10.8026 15.459 10.6451C13.7977 9.86723 11.9516 9.43005 9.99997 9.43005C8.04837 9.43005 6.20227 9.86723 4.54089 10.6451C4.2045 10.8026 3.99996 11.1464 3.99996 11.5178V14C3.99996 14.2761 4.22382 14.5 4.49996 14.5H15.5C15.7761 14.5 16 14.2761 16 14V11.5178ZM15 11.5344V13.5H4.99996V11.5344C6.52384 10.8269 8.21334 10.43 9.99997 10.43C11.7866 10.43 13.4761 10.8269 15 11.5344Z" fill="white"/>
+</svg>

+ 43 - 0
src/components/SelectReportClassify.vue

@@ -0,0 +1,43 @@
+<script setup>
+import { apiCustomerUser } from '@/api/customer'
+
+const model=defineModel()
+const props=defineProps({
+  props:{},
+  clearable:{
+    type:Boolean,
+    default:false
+  }
+})
+const emits=defineEmits(['change'])
+
+const options=ref([])
+async function getData(){
+  const res=await apiCustomerUser.reportClassify()
+  if(res.Ret!=200) return
+  const arr=res.Data||[]
+  options.value=arr
+}
+getData()
+
+function handleChange(){
+  emits('change')
+}
+
+</script>
+<template>
+  <el-cascader
+    :options="options"
+    :props="{
+      value: 'Id',
+      label: 'ClassifyName',
+      children: 'Child',
+      ...props.props
+    }"
+    v-model="model"
+    placeholder="报告类型"
+    collapse-tags
+    :clearable="clearable"
+    @change="handleChange"
+  />
+</template>

+ 0 - 20
src/hooks/login/index.js

@@ -1,20 +0,0 @@
-import router from '@/router'
-
-export function useLogin(){
-
-  function loginOut(){
-    localStorage.setItem('auth','');
-    localStorage.setItem('userName','');
-    localStorage.setItem('Role','');
-    localStorage.setItem('RoleType','');
-    localStorage.setItem('AdminId','');
-    localStorage.setItem('AdminName','');
-    localStorage.setItem('ManageType','');
-    localStorage.setItem('RoleIdentity','')
-    router.replace("/login");
-  }
-
-  return {
-    loginOut
-  }
-}

+ 26 - 6
src/layout/components/HeaderWrap.vue

@@ -20,12 +20,16 @@ const breadcrumbArr=computed(()=>{
   }
   return temarr
 })
+function handleClickBreadcrumb(e){
+  // router.push()
+}
+
 
+// 阅读消息
 function handleReadNotice(){
-  apiSystemMessage.msgRead().then(res=>{
-    
-  })
+  apiSystemMessage.msgRead()
 }
+const hasUnRead=ref(false)//是否有未读
 
 
 </script>
@@ -40,7 +44,7 @@ function handleReadNotice(){
     <el-icon size="26px" v-else @click="menuCloseChange"><i-ep-Fold /></el-icon>
     <!-- 面包屑 -->
     <el-breadcrumb separator="/" style="margin-left: 30px">
-      <el-breadcrumb-item v-for="item in breadcrumbArr" :key="item.title">{{
+      <el-breadcrumb-item v-for="item in breadcrumbArr" :key="item.title" @click="handleClickBreadcrumb(item)">{{
         item.title
       }}</el-breadcrumb-item>
     </el-breadcrumb>
@@ -48,9 +52,11 @@ function handleReadNotice(){
     <!-- 通知 -->
     <el-popover placement="bottom" :width="500" trigger="click" @show="handleReadNotice" v-if="hasPermission('message:info')">
       <template #reference>
-        <svg-icon name="notification-filled" size="18px"></svg-icon>
+        <div :class="['system-notice-box',hasUnRead?'system-notice-box_red':'']">
+          <svg-icon name="notification-filled" size="18px"></svg-icon>
+        </div>
       </template>
-      <NoticeWrap />
+      <NoticeWrap @change="e=>hasUnRead=e"/>
     </el-popover>
   </div>
 </template>
@@ -82,5 +88,19 @@ function handleReadNotice(){
   .content {
     flex: 1;
   }
+  .system-notice-box{
+    position: relative;
+    &.system-notice-box_red::after{
+      content: '';
+      display: block;
+      width: 6px;
+      height: 6px;
+      background-color: #f00;
+      border-radius: 6px;
+      position: absolute;
+      top: -2px;
+      right: -2px;
+    }
+  }
 }
 </style>

+ 11 - 5
src/layout/components/LeftWrap.vue

@@ -1,10 +1,8 @@
 <script setup>
 import { useRouter } from 'vue-router'
 import { useLayoutState } from '../hooks/index'
-import { useLogin } from '@/hooks/login/index'
 import {apiSystemRole} from '@/api/system'
 
-const { loginOut } = useLogin()
 const router = useRouter()
 
 const { menuClose } = useLayoutState()
@@ -34,10 +32,18 @@ function logout() {
     '提示',
     { type: 'warning', }
   ).then(() => {
-    loginOut()
+    localStorage.removeItem('token')
+    localStorage.removeItem('userName')
+    router.replace('/login')
   }).catch()
 }
 
+function getMenuIcon(item){
+  if(item.Name==='用户列表') return 'menu/user'
+  if(item.Name==='潜在用户列表') return 'menu/usergroup'
+  return 'menu/setting'
+}
+
 </script>
 
 <template>
@@ -54,7 +60,7 @@ function logout() {
           :index="level1.Path"
           v-if="level1.Children.length===0"
         >
-          <el-icon><i-ep-location /></el-icon>
+          <svg-icon :name="getMenuIcon(level1)" style="font-size:16px;"></svg-icon>
           <span>{{ level1.Name }}</span>
         </el-menu-item>
         <el-sub-menu
@@ -62,7 +68,7 @@ function logout() {
           v-if="level1.Children.length>0"
         >
           <template #title>
-            <el-icon><i-ep-location /></el-icon>
+            <svg-icon :name="getMenuIcon(level1)" style="font-size:16px"></svg-icon>
             <span>{{ level1.Name }}</span>
           </template>
           <el-menu-item

+ 4 - 0
src/layout/components/NoticeWrap.vue

@@ -4,11 +4,15 @@ import { useRouter } from 'vue-router'
 
 const router=useRouter()
 
+const emits=defineEmits(['change'])
+
 const list = ref([])
 function getMsgList() {
   apiSystemMessage.list().then(res => {
     if (res.Ret === 200) {
       list.value = res.Data.List || []
+      const hasUnRead=list.value.some(item=>!item.IsRead)
+      emits('change',hasUnRead)
     }
   })
 }

+ 44 - 35
src/views/Login.vue

@@ -1,12 +1,12 @@
 <script setup>
-import {apiSystemCommon,apiSystemRole} from '@/api/system'
+import { apiSystemCommon, apiSystemRole } from '@/api/system'
 import md5 from 'js-md5'
-import {Base64 } from 'js-base64'
+import { Base64 } from 'js-base64'
 import { useRouter } from 'vue-router'
 
-const router=useRouter()
+const router = useRouter()
 
-const ruleFormRef=ref(null)
+const ruleFormRef = ref(null)
 const formRules = {
   account: [{ required: true, message: '请输入用户名', trigger: 'blur' },],
   pwd: [{ required: true, message: '请输入密码', trigger: 'blur' },]
@@ -16,28 +16,28 @@ const formState = reactive({
   pwd: '',
   checked: false
 })
-async function submitForm(formEl){
+async function submitForm(formEl) {
   if (!formEl) return
   await formEl.validate((valid, fields) => {
     if (valid) {
       console.log('submit!')
-      const t=new Date().getTime()
-      const md5key='MiQM9YUdf89T2uIH'
+      const t = new Date().getTime()
+      const md5key = 'MiQM9YUdf89T2uIH'
       apiSystemCommon.login({
-        Username:formState.account,
-        password:md5(md5(formState.pwd)+md5key+`${t}`),
-        ReqTime:`${t}`,
-      }).then(res=>{
-        if(res.Ret===200){
-          if(formState.checked){
-            localStorage.setItem('account',Base64.encode(formState.account))
-            localStorage.setItem('pwd',Base64.encode(formState.pwd))
-          }else{
+        Username: formState.account,
+        password: md5(md5(formState.pwd) + md5key + `${t}`),
+        ReqTime: `${t}`,
+      }).then(res => {
+        if (res.Ret === 200) {
+          if (formState.checked) {
+            localStorage.setItem('account', Base64.encode(formState.account))
+            localStorage.setItem('pwd', Base64.encode(formState.pwd))
+          } else {
             localStorage.removeItem('account')
             localStorage.removeItem('pwd')
           }
-          localStorage.setItem('token',res.Data.Authorization)
-          localStorage.setItem('userName',res.Data.SysRealName)
+          localStorage.setItem('token', res.Data.Authorization)
+          localStorage.setItem('userName', res.Data.SysRealName)
           getMenuList()
         }
       })
@@ -48,15 +48,15 @@ async function submitForm(formEl){
 }
 
 // 获取菜单跳转到第一个菜单
-function getMenuList(){
-  apiSystemRole.menuData().then(res=>{
-    if(res.Ret===200){
-      const arr=res.Data||[]
-      let path=''
-      if(arr[0]?.Children[0]){
-        path=arr[0].Children[0].Path
-      }else{
-        path=arr[0].Path
+function getMenuList() {
+  apiSystemRole.menuData().then(res => {
+    if (res.Ret === 200) {
+      const arr = res.Data || []
+      let path = ''
+      if (arr[0]?.Children[0]) {
+        path = arr[0].Children[0].Path
+      } else {
+        path = arr[0].Path
       }
       router.replace(path)
     }
@@ -64,17 +64,26 @@ function getMenuList(){
 }
 
 //获取用户记住的账号密码 如果有
-function getRememberedInfo(){
-  const account=localStorage.getItem('account')||null
-  const pwd=localStorage.getItem('pwd')||null
-  if(account){
-    formState.account=Base64.decode(account)
-    formState.pwd=Base64.decode(pwd)
-    formState.checked=true
+function getRememberedInfo() {
+  const account = localStorage.getItem('account') || null
+  const pwd = localStorage.getItem('pwd') || null
+  if (account) {
+    formState.account = Base64.decode(account)
+    formState.pwd = Base64.decode(pwd)
+    formState.checked = true
   }
 }
 getRememberedInfo()
 
+onMounted(() => {
+  document.onkeydown = (e) => {
+    let keyval = window.event.keyCode;
+    if (keyval === 13) {
+      submitForm(ruleFormRef.value)
+    }
+  };
+})
+
 
 </script>
 
@@ -109,7 +118,7 @@ getRememberedInfo()
         </el-form-item>
         <el-form-item>
           <el-button
-            style="width: 100%;margin-top:50px"
+            style="width: 100%; margin-top: 50px"
             type="primary"
             @click="submitForm(ruleFormRef)"
             >登录</el-button

+ 23 - 3
src/views/customer/PotentialUserList.vue

@@ -9,7 +9,9 @@ const router = useRouter()
 
 const filterState = reactive({
   regsiterTime: [],
-  readTime: []
+  readTime: [],
+  sortType:'',
+  sortVal:''
 })
 
 
@@ -29,12 +31,12 @@ const tableColOpt = [
   },
   {
     label: '最近一次阅读时间',
-    key: '',
+    key: 'LastUpdateTime',
     sort: true
   },
   {
     label: '累计阅读次数',
-    key: '',
+    key: 'ReadCnt',
     sort: true,
   },
 ]
@@ -52,6 +54,8 @@ async function getUserList() {
     Status: '潜在',
     RegisterStartDate: filterState.regsiterTime ? filterState.regsiterTime[0] : '',
     RegisterEndDate: filterState.regsiterTime ? filterState.regsiterTime[1] : '',
+    SortParam:filterState.sortType,
+    SortType:filterState.sortVal
   })
   tableLoading.value = false
   if (res.Ret === 200) {
@@ -67,8 +71,21 @@ function handlePageChange(e) {
 function handleTableSort(e) {
   // console.log(e);
   const { order, prop } = e//order:"descending",prop: "RegisterTime"
+  filterState.sortType=prop
+  if(!order){
+    filterState.sortVal=''
+  }else{
+    filterState.sortVal=order==='descending'?'desc':'asc'
+  }
+  handleFilterList()
 }
 
+function handleFilterList() {
+  page.value = 1
+  getUserList()
+}
+
+
 
 function handleEditUser(e) {
   router.push({
@@ -94,6 +111,7 @@ function handleEditUser(e) {
           start-placeholder="注册时间"
           end-placeholder="注册时间"
           value-format="YYYY-MM-DD"
+          @change="handleFilterList"
         />
       </div>
       <div style="width: 235px">
@@ -105,6 +123,7 @@ function handleEditUser(e) {
           start-placeholder="最近一次阅读时间"
           end-placeholder="最近一次阅读时间"
           value-format="YYYY-MM-DD"
+          @change="handleFilterList"
         />
       </div>
       <el-input
@@ -113,6 +132,7 @@ function handleEditUser(e) {
         :prefix-icon="Search"
         clearable
         style="max-width: 359px;margin-left:auto"
+        @input="handleFilterList"
       />
     </div>
     <div class="userlist-wrap" style="margin-top: 20px">

+ 1 - 1
src/views/customer/UserEdit.vue

@@ -67,7 +67,7 @@ const formState = reactive({
   phone: '',
   email: '',
   sellerId: '',
-  validTime: ['', ''],
+  validTime: '',
   company: '',
   isEnabled: false
 

+ 35 - 16
src/views/customer/UserList.vue

@@ -2,7 +2,7 @@
 import { Search, Plus } from '@element-plus/icons-vue'
 import { apiCustomerUser } from '@/api/customer'
 import { useRouter } from 'vue-router'
-import { ElMessage, ElMessageBox } from 'element-plus'
+import { ElMessage, ElMessageBox,dayjs } from 'element-plus'
 import EnableUser from './components/EnableUser.vue'
 
 const router = useRouter()
@@ -14,7 +14,9 @@ const filterState = reactive({
   register: '',
   subscribe: '',
   regsiterTime: [],
-  createTime: []
+  createTime: [],
+  sortType:'',
+  sortVal:''
 })
 
 
@@ -37,7 +39,8 @@ const tableColOpt = [
   },
   {
     label: '用户状态',
-    key: 'Status'
+    key: 'Status',
+    width: '100px'
   },
   {
     label: '有效期',
@@ -46,12 +49,14 @@ const tableColOpt = [
   },
   {
     label: '到期时长',
-    key: 'ExpirationTime',
-    sort: true
+    key: 'RestDate',
+    sort: true,
+    width: '120px'
   },
   {
     label: '是否注册',
-    key: 'IsRegistered'
+    key: 'IsRegistered',
+    width: '100px'
   },
   {
     label: '注册时间',
@@ -89,6 +94,8 @@ async function getUserList() {
     RegisterEndDate: filterState.regsiterTime ? filterState.regsiterTime[1] : '',
     CreateStartDate: filterState.createTime ? filterState.createTime[0] : '',
     CreateEndDate: filterState.createTime ? filterState.createTime[1] : '',
+    SortParam:filterState.sortType,
+    SortType:filterState.sortVal
   })
   tableLoading.value = false
   if (res.Ret === 200) {
@@ -104,6 +111,14 @@ function handlePageChange(e) {
 function handleTableSort(e) {
   // console.log(e);
   const { order, prop } = e//order:"descending",prop: "RegisterTime"
+  filterState.sortType=prop
+  if(!order){
+    filterState.sortVal=''
+  }else{
+    filterState.sortVal=order==='descending'?'desc':'asc'
+  }
+  
+  handleFilterList()
 }
 function handleFilterList() {
   page.value = 1
@@ -160,7 +175,15 @@ function handleDelUser(row) {
   }).catch(() => { })
 }
 
-
+// 跳转详情
+function handleGoDetail(e){
+  router.push({
+    path:'/customer/userDetail',
+    query:{
+      id:e.UserId
+    }
+  })
+}
 
 
 </script>
@@ -259,6 +282,7 @@ function handleDelUser(row) {
         element-loading-text="数据加载中..."
         v-loading="tableLoading"
         @sort-change="handleTableSort"
+        @row-click="handleGoDetail"
       >
         <el-table-column
           v-for="column in tableColOpt"
@@ -293,11 +317,6 @@ function handleDelUser(row) {
             <span v-else-if="column.key === 'IsSubscribed'">{{
               row.IsSubscribed ? "是" : "否"
             }}</span>
-            <span
-              v-else-if="column.key === 'RealName'"
-              @click="$router.push('/customer/userDetail?id=' + row.UserId)"
-              >{{ row[column.key] }}</span
-            >
             <span v-else-if="column.key === 'ValidStartTime'"
               >{{ formatTime(row.ValidStartTime, "YYYY-MM-DD") }}~{{
                 formatTime(row.ValidEndTime, "YYYY-MM-DD")
@@ -318,7 +337,7 @@ function handleDelUser(row) {
               v-permission="'user:edit'"
               type="primary"
               link
-              @click="handleEditUser(row)"
+              @click.stop="handleEditUser(row)"
               >编辑</el-button
             >
             <el-button
@@ -326,7 +345,7 @@ function handleDelUser(row) {
               type="danger"
               link
               v-if="row.Status"
-              @click="handleDisabledUser(row)"
+              @click.stop="handleDisabledUser(row)"
               >禁用</el-button
             >
             <el-button
@@ -334,14 +353,14 @@ function handleDelUser(row) {
               type="primary"
               link
               v-else
-              @click="handleEnableUser(row)"
+              @click.stop="handleEnableUser(row)"
               >启用</el-button
             >
             <el-button
               v-permission="'user:delete'"
               type="danger"
               link
-              @click="handleDelUser(row)"
+              @click.stop="handleDelUser(row)"
               >删除</el-button
             >
           </template>

+ 1 - 1
src/views/customer/components/OperationRecord.vue

@@ -19,7 +19,7 @@ getRecord()
 </script>
 
 <template>
-  <div class="customer-operationRecord-wrap">
+  <div class="customer-operationRecord-wrap" v-if="list.length>0">
     <el-timeline>
       <el-timeline-item
         v-for="item in list"

+ 43 - 81
src/views/customer/reportStatistic/Chart.vue

@@ -1,60 +1,39 @@
 <script setup>
-import { Search, Switch } from '@element-plus/icons-vue'
-import { lineChartRender, pieChartRender } from './utils/chartRender'
-import { apiCustomerStatistic } from '@/api/customer'
+import { Switch } from '@element-plus/icons-vue'
+import { apiCustomerUser } from '@/api/customer'
+import LineChart from './components/LineChart.vue'
+import PieChart from './components/PieChart.vue'
+
 
 const emits = defineEmits(['change'])
 
 const filterState = reactive({
-  permission: '',
   type: '',
-  time: '',
   keyword: ''
 })
 
-async function getLineChartData() {
-  const res = await apiCustomerStatistic.readRecordChartData({
-    StartDate: filterState.time ? filterState.time[0] : '',
-    EndDate: filterState.time ? filterState.time[1] : '',
-    ClassifyIds: '',
-    ChartPermissionIds: filterState.permission?filterState.permission.join(','):''
-  })
-  if (res.Ret === 200) {
-    const arr = res.Data.ReadCntList || []
-    let data = {
-      time: [],
-      value: []
-    }
-    arr.forEach(item => {
-      data.time.push(item.CreateDate)
-      data.value.push(item.Count)
+const userId = ref('')
+const options = ref([])
+const searchLoading = ref(false)
+async function handleSearch(query) {
+  if (query) {
+    searchLoading.value = true
+    const res = await apiCustomerUser.userList({
+      PageSize: 100,
+      CurrentIndex: 1,
+      KeyWord: query
     })
-    lineChartRender(data)
-
-    const pArr=res.Data.PermissionCntList || []
-    getPieChartData(pArr)
-  }
-
-}
-
-function getPieChartData(data) {
-  let arr = data.map(item => {
-    return {
-      name: item.PermissionName,
-      y: item.Count
+    searchLoading.value = false
+    if (res.Ret === 200) {
+      const arr = res.Data.List || []
+      options.value = arr
+    } else {
+      options.value = []
     }
-  })
-  pieChartRender(arr)
+  }
 }
 
-onMounted(() => {
-  getLineChartData()
-  // getPieChartData()
-})
 
-function handleRefreshData(){
-  getLineChartData()
-}
 
 </script>
 
@@ -68,43 +47,34 @@ function handleRefreshData(){
         @click="emits('change', 'chart')"
         >数据表</el-button
       >
-      <select-permission
-        v-model="filterState.permission"
-        :props="{
-          emitPath: false,
-          multiple: true,
-        }"
-        clearable
-        @change="handleRefreshData"
-      />
-      <span style="width: 235px">
-        <el-date-picker
-          style="width: 235px"
-          v-model="filterState.time"
-          type="daterange"
-          range-separator="至"
-          start-placeholder="开始时间"
-          end-placeholder="结束时间"
-          value-format="YYYY-MM-DD"
-          @change="handleRefreshData"
-        />
-      </span>
-      <el-input
+      <el-select
+        v-model="userId"
+        filterable
+        remote
         placeholder="姓名/手机号/邮箱"
-        v-model="filterState.keyword"
-        :prefix-icon="Search"
-        clearable
+        :remote-method="handleSearch"
+        :loading="searchLoading"
         style="max-width: 359px; margin-left: auto"
-      />
+        clearable
+      >
+        <template #prefix>
+          <el-icon><i-ep-Search /></el-icon>
+        </template>
+
+        <el-option
+          v-for="item in options"
+          :key="item.UserId"
+          :label="item.RealName"
+          :value="item.UserId"
+        />
+      </el-select>
     </div>
     <div class="content-wrap">
       <div class="bg-white item-wrap">
-        <h3 style="text-align: center">用户阅读量统计图</h3>
-        <div class="chart-box" id="chartBox1"></div>
+        <LineChart :userId="userId"/>
       </div>
       <div class="bg-white item-wrap">
-        <h3 style="text-align: center">用户阅读品种分布图</h3>
-        <div class="chart-box" id="chartBox2"></div>
+        <PieChart :userId="userId"/>
       </div>
     </div>
   </div>
@@ -120,14 +90,6 @@ function handleRefreshData(){
   .item-wrap {
     margin-top: 20px;
     flex: 1;
-    height: calc(100vh - 200px);
-    padding-top: 30px;
-    padding-left: 30px;
-    padding-right: 30px;
-    .chart-box {
-      margin-top: 80px;
-      height: 400px;
-    }
   }
 }
 </style>

+ 21 - 2
src/views/customer/reportStatistic/List.vue

@@ -30,7 +30,7 @@ const tableColOpt = [
   },
   {
     label: '营业部/销售',
-    key: ''
+    key: 'SellerName'
   },
   {
     label: '用户状态',
@@ -66,6 +66,8 @@ async function getUserList() {
     RegisterEndDate: filterState.regsiterTime ? filterState.regsiterTime[1] : '',
     CreateStartDate: filterState.createTime ? filterState.createTime[0] : '',
     CreateEndDate: filterState.createTime ? filterState.createTime[1] : '',
+    SortParam:filterState.sortType,
+    SortType:filterState.sortVal
   })
   tableLoading.value = false
   if (res.Ret === 200) {
@@ -81,6 +83,17 @@ function handlePageChange(e) {
 function handleTableSort(e) {
   // console.log(e);
   const { order, prop } = e//order:"descending",prop: "RegisterTime"
+  filterState.sortType=prop
+  if(!order){
+    filterState.sortVal=''
+  }else{
+    filterState.sortVal=order==='descending'?'desc':'asc'
+  }
+  handleFilterList()
+}
+function handleFilterList() {
+  page.value = 1
+  getUserList()
 }
 
 
@@ -105,11 +118,13 @@ function handleShowDetail(e){
           multiple: true,
         }"
         clearable
+        @change="handleFilterList"
       />
       <el-select
         placeholder="用户状态"
         v-model="filterState.status"
         style="width: 165px"
+        @change="handleFilterList"
       >
         <el-option label="启用" :value="1"></el-option>
         <el-option label="禁用" :value="0"></el-option>
@@ -118,6 +133,7 @@ function handleShowDetail(e){
         placeholder="注册状态"
         v-model="filterState.register"
         style="width: 165px"
+        @change="handleFilterList"
       >
         <el-option label="是" value="是"></el-option>
         <el-option label="否" value="否"></el-option>
@@ -126,6 +142,7 @@ function handleShowDetail(e){
         placeholder="是否关注公众号"
         v-model="filterState.register"
         style="width: 165px"
+        @change="handleFilterList"
       >
         <el-option label="是" value="是"></el-option>
         <el-option label="否" value="否"></el-option>
@@ -139,6 +156,7 @@ function handleShowDetail(e){
           start-placeholder="注册时间"
           end-placeholder="注册时间"
           value-format="YYYY-MM-DD"
+          @change="handleFilterList"
         />
       </span>
       <span style="width: 235px">
@@ -150,6 +168,7 @@ function handleShowDetail(e){
           start-placeholder="创建时间"
           end-placeholder="创建时间"
           value-format="YYYY-MM-DD"
+          @change="handleFilterList"
         />
       </span>
       <el-input
@@ -158,6 +177,7 @@ function handleShowDetail(e){
         :prefix-icon="Search"
         clearable
         style="max-width: 359px;margin-left:auto"
+        @input="handleFilterList"
       />
     </div>
     <div class="userlist-wrap" style="margin-top: 20px">
@@ -175,7 +195,6 @@ function handleShowDetail(e){
           :key="column.key"
           :prop="column.key"
           :label="column.label"
-          align="center"
           :sortable="column.sort ? 'custom' : false"
         >
           <template #default="{ row }">

+ 103 - 0
src/views/customer/reportStatistic/components/LineChart.vue

@@ -0,0 +1,103 @@
+<script setup>
+import { dayjs } from "element-plus"
+import { apiCustomerStatistic } from '@/api/customer'
+import { lineChartRender } from '../utils/chartRender'
+
+const props = defineProps({
+  userId: ''
+})
+
+const permission = ref('')
+const classify = ref('')
+const time = ref([dayjs().subtract(1, 'year').format('YYYY-MM-DD'), dayjs().format('YYYY-MM-DD')])
+
+
+function getChartData() {
+  apiCustomerStatistic.readTimesChartData({
+    UserId: props.userId || 0,
+    ChartPermissionIds: permission.value ? permission.value.join(',') : '',
+    ClassifyIds: classify.value ? classify.value.join(',') : '',
+    StartDate: time.value ? time.value[0] : '',
+    EndDate: time.value ? time.value[1] : '',
+  }).then(res => {
+    if (res.Ret === 200) {
+      const arr = res.Data || []
+      let data = {
+        time: [],
+        value: []
+      }
+      arr.forEach(item => {
+        data.time.push(item.CreateDate)
+        data.value.push(item.Count)
+      })
+      lineChartRender(data)
+    }
+  })
+}
+getChartData()
+
+watch(
+  () => props.userId,
+  (n) => {
+    getChartData()
+  }
+)
+
+
+
+</script>
+
+<template>
+  <div class="read-times-wrap">
+    <div class="flex filter-wrap">
+      <select-permission
+        v-model="permission"
+        :props="{
+          emitPath: false,
+          multiple: true,
+        }"
+        clearable
+        @change="getChartData"
+      />
+      <select-report-classify
+        v-model="classify"
+        :props="{
+          emitPath: false,
+          multiple: true,
+        }"
+        clearable
+        @change="getChartData"
+      />
+      <span style="width: 235px">
+        <el-date-picker
+          style="width: 235px"
+          v-model="time"
+          type="daterange"
+          range-separator="至"
+          start-placeholder="开始时间"
+          end-placeholder="结束时间"
+          value-format="YYYY-MM-DD"
+          @change="getChartData"
+        />
+      </span>
+    </div>
+    <h3 style="text-align: center">用户阅读量统计图</h3>
+    <div class="chart-box" id="chartBox1"></div>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.filter-wrap {
+  gap: 10px;
+}
+.read-times-wrap {
+  height: calc(100vh - 200px);
+  padding-top: 30px;
+  padding-left: 30px;
+  padding-right: 30px;
+  .chart-box {
+    margin-top: 80px;
+    height: 400px;
+  }
+}
+</style>

+ 55 - 0
src/views/customer/reportStatistic/components/PieChart.vue

@@ -0,0 +1,55 @@
+<script setup>
+import { apiCustomerStatistic } from '@/api/customer'
+import { pieChartRender } from '../utils/chartRender'
+
+const props = defineProps({
+  userId: ''
+})
+
+function getChartData() {
+  apiCustomerStatistic.readPermissionChartData({
+    UserId: props.userId || 0,
+  }).then(res => {
+    if (res.Ret === 200) {
+      const arr = res.Data || []
+      let data = arr.map(item => {
+        return {
+          ...item,
+          name: item.PermissionName,
+          y: item.Count,
+        }
+      })
+      pieChartRender(data)
+    }
+  })
+}
+getChartData()
+
+watch(
+  () => props.userId,
+  (n) => {
+    getChartData()
+  }
+)
+
+</script>
+
+<template>
+  <div class="read-permission-wrap">
+    <h3 style="text-align: center">用户阅读品种分布图</h3>
+    <div class="chart-box" id="chartBox2"></div>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.read-permission-wrap {
+  height: calc(100vh - 200px);
+  padding-top: 30px;
+  padding-left: 30px;
+  padding-right: 30px;
+  .chart-box {
+    margin-top: 80px;
+    height: 400px;
+  }
+}
+</style>

+ 23 - 4
src/views/customer/reportStatistic/components/UserStatisticDetail.vue

@@ -39,13 +39,15 @@ const pageSize = ref(10)
 const tableLoading = ref(false)
 const totals = ref(0)
 const permissionIds = ref('')
+const classifyIds = ref('')
 async function getList() {
   tableLoading.value = true
   const res = await apiCustomerStatistic.readRecordInfo({
     UserId: props.userId,
     PageSize: pageSize.value,
     CurrentIndex: page.value,
-    ChartPermissionIds: permissionIds.value?permissionIds.value.join(','):'',
+    ChartPermissionIds: permissionIds.value ? permissionIds.value.join(',') : '',
+    classify_id2: classifyIds.value ? classifyIds.value.join(',') : '',
   })
   tableLoading.value = false
   if (res.Ret === 200) {
@@ -66,7 +68,7 @@ function handleRefreshList() {
 
 watch(() => show.value, (n) => {
   if (n) {
-    permissionIds.value=''
+    permissionIds.value = ''
     handleRefreshList()
   }
 })
@@ -74,7 +76,7 @@ watch(() => show.value, (n) => {
 </script>
 
 <template>
-  <el-dialog v-model="show" width="800" title="研报阅读统计" draggable>
+  <el-dialog v-model="show" width="850" title="研报阅读统计" draggable>
     <div class="user-statistic-detail-wrap">
       <div>
         <select-permission
@@ -86,6 +88,16 @@ watch(() => show.value, (n) => {
           clearable
           @change="handleRefreshList"
         />
+        <select-report-classify
+          style="margin-left: 5px"
+          v-model="classifyIds"
+          :props="{
+            emitPath: false,
+            multiple: true,
+          }"
+          clearable
+          @change="handleRefreshList"
+        />
       </div>
       <div class="total-text">公有{{ totals }}条阅读记录</div>
       <el-table
@@ -100,7 +112,14 @@ watch(() => show.value, (n) => {
           :key="column.key"
           :prop="column.key"
           :label="column.label"
-        />
+        >
+          <template #default="{ row }">
+            <span v-if="column.key === 'CreateTime'">{{
+              formatTime(row.CreateTime)
+            }}</span>
+            <span v-else>{{ row[column.key] }}</span>
+          </template>
+        </el-table-column>
       </el-table>
       <el-pagination
         background

+ 9 - 12
src/views/customer/reportStatistic/utils/chartRender.js

@@ -42,12 +42,15 @@ export function lineChartRender(data) {
       },
       tickWidth: 1,
       tickLength: 5,
+      lineColor:'#bfbfbf',
+      tickColor:'#bfbfbf',
     },
     yAxis: [
       {
         gridLineWidth: 0,
         lineWidth: 1,
-        lineColor: "#666666",
+        lineColor:'#bfbfbf',
+        tickColor:'#bfbfbf',
         tickWidth: 1,
         tickLength: 5,
         labels: {
@@ -101,6 +104,10 @@ export function pieChartRender(data) {
       x: 0,
       y: 0,
     },
+    tooltip: {
+      shared: false,
+      pointFormat: '<span>{point.y}次:{point.Percent}%<br/>'
+    },
     plotOptions: {
       pie: {
         allowPointSelect: true,
@@ -117,20 +124,10 @@ export function pieChartRender(data) {
       {
         type: "pie",
         innerSize: "80%",
-        name: "占比",
+        name: "",
         colorByPoint: true,
         borderRadius: 0,
         data: data,
-        // data: [{
-        //   name: '1',
-        //   y: 60,
-        // }, {
-        //   name: '2',
-        //   y: 20
-        // }, {
-        //   name: '3',
-        //   y: 10
-        // }]
       },
     ],
   };

+ 12 - 7
src/views/system/userList/components/EditUser.vue

@@ -9,11 +9,16 @@ const props=defineProps({
 })
 const emits=defineEmits(['success'])
 
+// 编辑admin 用户标识
+const isEditAdmin=computed(()=>{
+  return props.data&&props.data.SysUserName==='admin'
+})
+
 watch(
   ()=>show.value,
   (n)=>{
     if(n&&props.data){
-      console.log(props.data);
+      // console.log(props.data);
       formState=reactive({
         account: props.data.SysUserName,
         pwd: '',
@@ -188,10 +193,10 @@ async function handleSave() {
           v-model="formState.account"
           placeholder="建议使用邮箱前缀或者手机号码"
           clearable
-          :disabled="props.data.SysUserName==='admin'"
+          :disabled="isEditAdmin"
         />
       </el-form-item>
-      <el-form-item label="登录密码" prop="pwd" v-if="!props.data||props.data.SysUserName!=='admin'">
+      <el-form-item label="登录密码" prop="pwd" v-if="!props.data||!isEditAdmin">
         <el-input
           v-model.trim="formState.pwd"
           placeholder="6-12位数字与字母的组合"
@@ -204,7 +209,7 @@ async function handleSave() {
           v-model="formState.name"
           placeholder="请输入用户名称"
           clearable
-          :disabled="props.data.SysUserName==='admin'"
+          :disabled="isEditAdmin"
         />
       </el-form-item>
       <el-form-item label="手机号码" prop="mobile" class="mobile-input-item">
@@ -229,7 +234,7 @@ async function handleSave() {
           style="width: 60%; margin-left: 5%"
         />
       </el-form-item>
-      <el-form-item label="所属部门" prop="depart" v-if="props.data.SysUserName!=='admin'">
+      <el-form-item label="所属部门" prop="depart" v-if="!isEditAdmin">
         <el-cascader
           style="width: 100%"
           :options="departArr"
@@ -250,7 +255,7 @@ async function handleSave() {
         <el-input v-model="formState.email" placeholder="请输入邮箱" />
       </el-form-item>
       <el-form-item label="分配角色" prop="role">
-        <el-select v-model="formState.role" placeholder="分配角色" :disabled="props.data.SysUserName==='admin'">
+        <el-select v-model="formState.role" placeholder="分配角色" :disabled="isEditAdmin">
           <el-option
             v-for="item in roleArr"
             :key="item.SysRoleId"
@@ -269,7 +274,7 @@ async function handleSave() {
           />
         </div>
       </el-form-item>
-      <el-form-item label="状态" prop="status" v-if="props.data.SysUserName!=='admin'">
+      <el-form-item label="状态" prop="status" v-if="!isEditAdmin">
         <el-radio-group v-model="formState.status">
           <el-radio :value="true">启用</el-radio>
           <el-radio :value="false">禁用</el-radio>

+ 5 - 3
src/views/system/userList/components/MoveUserDepart.vue

@@ -25,9 +25,11 @@ watch(() => show.value, (newval) => {
   }
 })
 
-let formRef = ref(null)
 async function handleSubmitForm() {
-  await formRef.value.validate()
+  if(!depart.value){
+    ElMessage.warning('请选择分组')
+    return
+  }
   const res=await apiSystemUser.moveUserDepart({
     sysuserid:props.data.SysUserId,
     SysDepartmentId:depart.value
@@ -48,7 +50,7 @@ async function handleSubmitForm() {
     title="移动分组"
   >
     <div class="dialog-content">
-      <el-form ref="formRef" :model="form" label-width="100px">
+      <el-form label-width="100px">
         <el-form-item label="账号">
           <span>{{ props.data.SysRealName }}</span>
         </el-form-item>