Forráskód Böngészése

Merge branch 'ch/jinrui1.0' of eta_mini/eta_mini_crm_front into master_jr

leichen 1 napja
szülő
commit
cd34a90cd2
35 módosított fájl, 1168 hozzáadás és 1336 törlés
  1. 0 12
      src/api/customer/modules/statistic.js
  2. 14 6
      src/api/customer/modules/user.js
  3. 2 6
      src/api/index.js
  4. 1 3
      src/api/system/index.js
  5. 0 12
      src/api/system/message.js
  6. 4 0
      src/api/system/user.js
  7. 69 41
      src/components/AllUserForDepart.vue
  8. 31 18
      src/components/ProvinceCitySelect.vue
  9. 5 5
      src/components/SelectReportClassify.vue
  10. 5 10
      src/layout/components/HeaderWrap.vue
  11. 0 8
      src/router/modules/customer.js
  12. 3 3
      src/utils/buttonConfig.js
  13. 3 3
      src/views/Login.vue
  14. 22 11
      src/views/customer/CompanyEdit.vue
  15. 138 104
      src/views/customer/PotentialUserList.vue
  16. 10 30
      src/views/customer/UserEdit.vue
  17. 192 176
      src/views/customer/UserList.vue
  18. 86 64
      src/views/customer/UserSearch.vue
  19. 202 0
      src/views/customer/components/AddContact.vue
  20. 73 104
      src/views/customer/components/ContactTable.vue
  21. 109 0
      src/views/customer/components/MoveUser.vue
  22. 162 0
      src/views/customer/components/ToFormalize.vue
  23. 0 95
      src/views/customer/reportStatistic/Chart.vue
  24. 0 15
      src/views/customer/reportStatistic/Index.vue
  25. 0 258
      src/views/customer/reportStatistic/List.vue
  26. 0 103
      src/views/customer/reportStatistic/components/LineChart.vue
  27. 0 55
      src/views/customer/reportStatistic/components/PieChart.vue
  28. 10 16
      src/views/customer/reportStatistic/components/UserStatisticDetail.vue
  29. 0 140
      src/views/customer/reportStatistic/utils/chartRender.js
  30. 2 2
      src/views/system/AuthSet.vue
  31. 1 1
      src/views/system/RoleList.vue
  32. 4 4
      src/views/system/userList/Index.vue
  33. 5 3
      src/views/system/userList/components/DepartWrap.vue
  34. 13 26
      src/views/system/userList/components/EditUser.vue
  35. 2 2
      vite.config.js

+ 0 - 12
src/api/customer/modules/statistic.js

@@ -1,21 +1,9 @@
 import { get, post } from "@/api/index";
 
 export default {
-  //用户阅读统计列表数据
-  readRecordList: params => {
-    return get("/read/list", params);
-  },
   // 用户阅读统计详情数据
   readRecordInfo: params => {
     return get("/report/read_record", params);
   },
-  // 用户阅读量统计图数据
-  readTimesChartData:params=>{
-    return get('/read/readCntChart',params)
-  },
-  // 用户阅读品种分布图数据
-  readPermissionChartData:params=>{
-    return get('/read/readPermissionChart',params)
-  },
   
 };

+ 14 - 6
src/api/customer/modules/user.js

@@ -37,8 +37,16 @@ export default {
     return post('/users/remove',params)
   },
   //潜在用户转客户
-  potentialUserEdit:params=>{
-    return post('/user/potential/edit',params)
+  usersTransFormal:params=>{
+    return post('/users/trans_formal',params)
+  },
+  //移动联系人
+  usersMove:params=>{
+    return post('/users/move',params)
+  },
+  //移动联系人
+  usersReportClassifyTree:params=>{
+    return get('/report/classify_tree',params)
   },
   // 客户全局搜索
   userGlobalSearch:params=>{
@@ -48,10 +56,6 @@ export default {
   sellerDepartmentList:params=>{
     return get('/seller/list',params)
   },
-  // 导出营业部门列表
-  potentialExportExcel:params=>{
-    return get('/user/potential/export/excel',params)
-  },
   // 机构详情
   companyInfo: params => {
     return get("/company/detail", params);
@@ -80,4 +84,8 @@ export default {
   companyRemove:params=>{
     return post('/company/remove',params)
   },
+  // 机构搜索
+  companySearch:params=>{
+    return get('/company/search',params)
+  },
 };

+ 2 - 6
src/api/index.js

@@ -25,11 +25,7 @@ _axios.interceptors.request.use(
       config.headers.Authorization = auth;
     }
     // config.headers.Authorization=localStorage.getItem('token')||''
-    
-    // 检查请求路径并设置responseType
-    if (config.url.endsWith('/user/potential/export/excel')) {
-      config.responseType = 'blob'; // 如果是导出excel的请求,则设置responseType为blob
-    }
+
     return config;
   },
   function (error) {
@@ -44,7 +40,7 @@ _axios.interceptors.response.use(
     // Do something with response data
     let data
     // 如果是是导出excel的请求,则不进行解密操作
-    if(import.meta.env.MODE==='production' && !response.config.url.endsWith('/user/potential/export/excel')){
+    if(import.meta.env.MODE==='production'){
       data=JSON.parse(CryptoJS.Des3Decrypt(response.data,import.meta.env.VITE_APP_RESPONSE_DES_KEY));//解密
     }else{
       data=response.data

+ 1 - 3
src/api/system/index.js

@@ -2,13 +2,11 @@ import apiSystemCommon from './common'
 import apiSystemDepart from './depart'
 import apiSystemUser from './user'
 import apiSystemRole from './role'
-import apiSystemMessage from './message'
 
 export {
     apiSystemCommon,
     apiSystemDepart,
     apiSystemUser,
-    apiSystemRole,
-    apiSystemMessage
+    apiSystemRole
 }
 

+ 0 - 12
src/api/system/message.js

@@ -1,12 +0,0 @@
-import { get, post } from "@/api/index";
-
-export default {
-  //系统消息
-  list: () => {
-    return get("/sys_message/list", {});
-  },
-  // 消息已读
-  msgRead: (params) => {
-    return post("/sys_message/read", params);
-  },
-};

+ 4 - 0
src/api/system/user.js

@@ -33,6 +33,10 @@ export default {
   allUserForDepart:params=>{
     return get('/seller/list',params)
   },
+  // 获取系统中所有用户 按部门分级
+  sellerUserTree:params=>{
+    return get('/seller/user_tree',params)
+  },
   // 系统用户修改密码
   modifyPwd:params=>{
     return post('/sys_user/reset_my_pass',params)

+ 69 - 41
src/components/AllUserForDepart.vue

@@ -1,71 +1,99 @@
 <script setup>
-import {apiSystemUser} from '@/api/system'
+import { apiSystemUser } from '@/api/system';
+import { ref } from 'vue';
 
 const model=defineModel()
-const props=defineProps({
-  props:{},
-  clearable:{
-    type:Boolean,
-    default:false
+// 定义组件的 props
+const props = defineProps({
+  props: Object,
+  clearable: {
+    type: Boolean,
+    default: false
   },
-  filterable:{
-    type:Boolean,
-    default:true
+  filterable: {
+    type: Boolean,
+    default: true
   },
-  placeholder:{
-    type:String,
-    default:'营业部/销售'
+  placeholder: {
+    type: String,
+    default: '营业部/销售'
   },
-  onlySelectUser:{//是否只能选择销售用户
-    type:Boolean,
-    default:false
+  size: {
+    type: String,
+    default: 'default'
+  },
+  onlySelectUser: {
+    type: Boolean,
+    default: false
   }
-})
-const emits=defineEmits(['change'])
+});
+
+// 定义组件的 emits
+const emits = defineEmits(['change']);
+
+// 定义选项数据
+const options = ref([]);
 
-const options=ref([])
-function formatData(arr){
+// 格式化数据函数
+function formatData(arr) {
   arr.forEach(item => {
-    item.label=item.SysUserId?item.SysRealName:item.SysDepartmentName
-    item.value=item.SysUserId?item.SysUserId:item.SysDepartmentId
-    if(props.onlySelectUser){//如果只能选择销售用户则将最后一级为部门的禁用
-      item.disabled=!item.SysUserId&&!item.ChildrenList?true:false
+    // 如果只能选择销售用户且当前项没有子项,则禁用部门节点
+    if (props.onlySelectUser && !item.Children.length) {
+      item.disabled = item.NodeType === 1;
     }
-    
-    if(item.ChildrenList){
-      formatData(item.ChildrenList)
+    if (item.NodeType === 1) { // 如果是部门节点,则NodeId乘10000以区分用户和部门节点
+      item.NodeId = item.NodeId * 10000;
+    }
+    if (item.Children) {
+      formatData(item.Children);
     }
   });
-  return arr
+  return arr;
 }
 
-async function getData(){
-  const res=await apiSystemUser.allUserForDepart({AllEnabled:false})//true显示所有销售(包含被禁用的)false表示仅显示启用的用户,默认false
-  if(res.Ret!=200) return
-  const arr=res.Data.List||[]
-  options.value=formatData(arr)
+// 获取数据函数
+async function fetchData() {
+  try {
+    const res = await apiSystemUser.sellerUserTree(); // true显示所有销售(包含被禁用的)false表示仅显示启用的用户,默认false
+    if (res.Ret === 200) {
+      const data = res.Data || [];
+      options.value = formatData(data);
+    } else {
+      // 处理错误情况,例如显示错误消息或进行其他操作
+      console.error('Failed to fetch data:', res);
+    }
+  } catch (error) {
+    // 处理异步调用中的错误
+    console.error('Error fetching data:', error);
+  }
 }
-getData()
 
-function handleChange(){
-  emits('change')
-}
+// 在组件加载时获取数据
+fetchData();
 
+// 处理选择变化
+function handleChange() {
+  emits('change');
+}
 </script>
+
 <template>
   <el-cascader
     :options="options"
     :props="{
-      value: 'value',
-      label: 'label',
-      children: 'ChildrenList',
+      value: 'NodeId',
+      label: 'NodeName',
+      children: 'Children',
       ...props.props
     }"
     v-model="model"
     :placeholder="props.placeholder"
     collapse-tags
-    :clearable="clearable"
-    :filterable="filterable"
+    :max-collapse-tags="1"
+    :size="props.size"
+    :show-all-levels="false"
+    :clearable="props.clearable"
+    :filterable="props.filterable"
     @change="handleChange"
   />
 </template>

+ 31 - 18
src/components/ProvinceCitySelect.vue

@@ -92,19 +92,32 @@ watch(
     },
     {immediate:true})
 
-function handleSelectChange(type){
-    if(type==='province'){
-        city.value = {cityKey:'',cityName:''}
-        area.value = {areaKey:'',areaName:''}
+function handleSelectChange(type) {
+    if (type === 'province') {
+        city.value = { cityKey: '', cityName: '' };
+        area.value = { areaKey: '', areaName: '' };
+    } else if (type === 'city' && props.showArea) {
+        area.value = { areaKey: '', areaName: '' };
     }
-    if(type==='city'&&props.showArea){
-        area.value = {areaKey:'',areaName:''}
+
+    // 根据选择的键更新对应的名称(如果需要)
+    if (type === 'province') {
+        const selectedProvince = provinceSource.value.find(p => p.provinceKey === province.value.provinceKey);
+        if (selectedProvince) {
+            province.value = selectedProvince;
+        }
+    } else if (type === 'city') {
+        const selectedCity = citySource.value.find(c => c.cityKey === city.value.cityKey);
+        if (selectedCity) {
+            city.value = selectedCity;
+        }
     }
-    emit('selected',{
-        province:{value:province.value.provinceName,provinceKey:province.value.provinceKey},
-        city:{value:city.value.cityName, cityKey:city.value.cityKey},
-        area:{value:area.value.areaName,}
-    })
+
+    emit('selected', {
+        province: { value: province.value.provinceName, provinceKey: province.value.provinceKey },
+        city: { value: city.value.cityName, cityKey: city.value.cityKey },
+        area: { value: area.value.areaName, areaKey: area.value.areaKey }
+    });
 }
 
 </script>
@@ -115,7 +128,7 @@ function handleSelectChange(type){
         <el-select
             placeholder="请选择省"
             filterable
-            v-model="province"
+            v-model="province.provinceKey"
             value-key="provinceKey"
             :disabled="props.disabled"
             @change="handleSelectChange('province')"
@@ -124,14 +137,14 @@ function handleSelectChange(type){
                 v-for="province in provinceSource"
                 :key="province.provinceKey"
                 :label="province.provinceName"
-                :value="province"
+                :value="province.provinceKey"
             />
         </el-select>
         <!-- 市选择器 -->
         <el-select
             placeholder="请选择市"
             filterable
-            v-model="city"
+            v-model="city.cityKey"
             value-key="cityKey"
             :disabled="props.disabled"
             @change="handleSelectChange('city')"
@@ -140,14 +153,14 @@ function handleSelectChange(type){
                 v-for="city in citySource"
                 :key="city.cityKey"
                 :label="city.cityName"
-                :value="city"
+                :value="city.cityKey"
             />
         </el-select>
         <!-- 区选择器 -->
-        <el-select v-if="showArea"
+        <el-select v-if="props.showArea"
             placeholder="请选择区"
             filterable
-            v-model="area"
+            v-model="area.areaKey"
             value-key="areaKey"
             :disabled="props.disabled"
             @change="handleSelectChange"
@@ -156,7 +169,7 @@ function handleSelectChange(type){
                 v-for="area in areaSource"
                 :key="area.areaKey"
                 :label="area.areaName"
-                :value="area"
+                :value="area.areaKey"
             />
         </el-select>
     </div>

+ 5 - 5
src/components/SelectReportClassify.vue

@@ -1,5 +1,5 @@
 <script setup>
-// import { apiReportCommon } from '@/api/report'
+import { apiCustomerUser } from '@/api/customer'
 
 const model=defineModel()
 const props=defineProps({
@@ -13,10 +13,10 @@ const emits=defineEmits(['change'])
 
 const options=ref([])
 async function getData(){
-  // const res=await apiReportCommon.classifyList()
-  // if(res.Ret!=200) return
-  // const arr=res.Data||[]
-  // options.value=arr
+  const res=await apiCustomerUser.usersReportClassifyTree()
+  if(res.Ret!=200) return
+  const arr=res.Data||[]
+  options.value=arr
 }
 getData()
 

+ 5 - 10
src/layout/components/HeaderWrap.vue

@@ -22,10 +22,10 @@ const breadcrumbArr=computed(()=>{
   return temarr
 })
 function handleClickBreadcrumb(e){
-  console.log(e);
+  const pathTitles = ['客户管理', '系统设置']
   if(e.path){
     router.push(e.path)
-  }else{
+  } else if(!pathTitles.includes(e.title) ) {
     window.location.reload();
   }
   // router.push()
@@ -59,9 +59,9 @@ function handleCommand (command) {
     <el-icon size="26px" v-else @click="menuCloseChange" color="#333333" style="margin-left: 200px;"><i-ep-Fold /></el-icon>
     <!-- 面包屑 -->
     <el-breadcrumb separator="/" style="margin-left: 30px">
-      <el-breadcrumb-item v-for="item,index in breadcrumbArr" :key="index" @click="handleClickBreadcrumb(item)">{{
-        item.title
-      }}</el-breadcrumb-item>
+      <el-breadcrumb-item v-for="item,index in breadcrumbArr" :key="index" @click="handleClickBreadcrumb(item)">
+        <span  :style="index > 0 ? 'color: #053CC9;' : ''">{{item.title}}</span>
+      </el-breadcrumb-item>
     </el-breadcrumb>
     <div class="content"></div>
     <!-- 通知 -->
@@ -99,11 +99,6 @@ function handleCommand (command) {
     margin-left: 20px;
     margin-right: 40px;
   }
-  :deep(.el-breadcrumb__item){
-    .el-breadcrumb__inner{
-      color: #053CC9;
-    }
-  }
 
   .content {
     flex: 1;

+ 0 - 8
src/router/modules/customer.js

@@ -93,14 +93,6 @@ export default[
           fromPath:'/customer/customer_list'
         },
       },
-      {
-        path:'userReportStatistic',
-        component:()=>import('@/views/customer/reportStatistic/Index.vue'),
-        name:"CustomerUserReportStatistic",
-        meta:{
-          title:'阅读统计'
-        },
-      },
       {
         path:'userTransform',
         component:()=>import('@/views/customer/UserEdit.vue'),

+ 3 - 3
src/utils/buttonConfig.js

@@ -9,9 +9,9 @@ export const customerManageBtn={
     customer_edit:'customer:edit', // 编辑客户
     customer_permissionSettings:'customer:permissionSettings', // 开启/关闭权限
     customer_delete:'customer:delete', // 删除客户
-    customer_add:'contact:add', // 添加/编辑联系人
-    customer_delete:'contact:delete', // 删除联系人
-    customer_move:'contact:move', // 移动联系人
+    contact_add:'contact:add', // 添加/编辑联系人
+    contact_delete:'contact:delete', // 删除联系人
+    contact_move:'contact:move', // 移动联系人
 
     /* 试用列表 */
     trial_list:'trial:list', // 查看列表

+ 3 - 3
src/views/Login.vue

@@ -103,8 +103,8 @@ onMounted(() => {
 
     <div class="form-wrap">
       <div class="form-content">
-        <h1 class="login-title">Login</h1>
-        <h3 class="title">运营管理系统</h3>
+        <!-- <h1 class="login-title">Login</h1> -->
+        <h4 class="title">金瑞期货</h4>
         <el-form
           ref="ruleFormRef"
           :model="formState"
@@ -142,7 +142,6 @@ onMounted(() => {
           </el-form-item>
         </el-form>
       </div>
-      <p class="bot-tips">期货投资咨询业务批准文号:证监许可【2011】1446号</p>
     </div>
   </div>
 </template>
@@ -196,6 +195,7 @@ onMounted(() => {
       margin-bottom: 36px;
     }
     .title {
+      text-align: center;
       margin-top: 0;
       font-size: 38px;
     }

+ 22 - 11
src/views/customer/CompanyEdit.vue

@@ -10,6 +10,9 @@ const router = useRouter()
 
 const CompanyId = ref(route.query.id || 0)
 
+const showAddContact = ref(false)
+const userInfo=ref({})
+
 const isView = computed(() => {
   return route.path === '/customer/companyDetail'
 })
@@ -21,6 +24,8 @@ const formRules = {
   CreditCode: [{ required: true, message: '社会信用码不能为空', trigger: 'blur' }],
   Industry: [{ required: true, message: '所属行业不能为空', trigger: 'blur' }],
   sellerId: [{ required: true, message: '营业部不能为空', trigger: 'blur' }],
+  province: [{ required: true, message: '省份不能为空', trigger: 'blur' }],
+  city: [{ required: true, message: '城市不能为空', trigger: 'blur' }],
 }
 const formState = reactive({
   CompanyName: '',
@@ -29,11 +34,13 @@ const formState = reactive({
   sellerId:'',
   province: '',
   city: '',
+  CityId: '',
+  ProvinceId: '',
 })
 
 
 const checkedIds = ref([])
-
+const AuthStatus = ref(1)
 function getUserInfo() {
   if (!CompanyId.value) return
   apiCustomerUser.companyInfo({
@@ -43,10 +50,13 @@ function getUserInfo() {
       const Detail = res.Data
       formState.CompanyName = Detail.CompanyName
       formState.CreditCode = Detail.CreditCode
+      AuthStatus.value = Detail.AuthStatus
       formState.Industry = Detail.Industry
       formState.sellerId = Detail.SellerId
       formState.province = Detail.Province
       formState.city = Detail.City
+      formState.ProvinceId = Detail.ProvinceId
+      formState.CityId = Detail.CityId
     }
   })
 }
@@ -114,13 +124,17 @@ async function handleSave(type) {
 
 
 function selectRegion(data) {
-  console.log(data);
-  
   formState.province = data.province.value;
   formState.ProvinceId = data.province.provinceKey;
   formState.city = data.city.value == '市' ? '' : data.city.value;
   formState.CityId = data.city.value == '市' ? '' : data.city.cityKey;
 }
+
+
+const contactTableRef = ref(null)
+function handleChangeList() {
+  contactTableRef.value.getUserList();
+}
 </script>
 
 <template>
@@ -140,7 +154,7 @@ function selectRegion(data) {
           </div>
           <div class="flex form-tr-box">
             <el-form-item label="投资者名称" prop="CompanyName">
-              <el-input v-model="formState.CompanyName" placeholder="请输入机构投资者名称全" />
+              <el-input v-model="formState.CompanyName" placeholder="请输入机构投资者名称全" />
             </el-form-item>
             <el-form-item label="社会信用码" prop="CreditCode">
               <el-input
@@ -176,13 +190,13 @@ function selectRegion(data) {
               />
             </el-form-item>
           </div>
-          <div class="flex form-tr-box" v-if="route.path !== '/customer/companyDetail'">
+          <div class="flex form-tr-box">
             <el-form-item label="权限" prop="CompanyId">
-              可查看所有报告
+              {{ AuthStatus === 1 ? '可查看所有报告' : '无报告查看权限' }}
             </el-form-item>
           </div>
           <div class="flex form-tr-box color" v-if="route.path !== '/customer/companyDetail'">
-            *温馨提示:请确保投资者名称、社会信用码的正确性
+            *温馨提示:请确保投资者名称、社会信用码的正确性 
           </div>
         </el-form>
       </div>
@@ -208,11 +222,8 @@ function selectRegion(data) {
         <div class="text-icon"></div>
         <h3 class="text">联系人信息</h3>
       </div>
-      <el-button type="primary" color="#0052D9" style="width: 120px; margin-top: 10px;" @click="handleSave"
-          >添加联系人</el-button
-        >
       <ContactTable
-        v-model:checkedIds="checkedIds"
+        ref="contactTableRef"
       />
     </div>
   </div>

+ 138 - 104
src/views/customer/PotentialUserList.vue

@@ -5,6 +5,7 @@ import { useRouter } from 'vue-router'
 import { dayjs, ElMessage, ElMessageBox } from 'element-plus'
 import EnableUser from './components/EnableUser.vue'
 import UserStatisticDetail from './reportStatistic/components/UserStatisticDetail.vue'
+import ToFormalize from './components/ToFormalize.vue'
 
 const router = useRouter()
 
@@ -23,22 +24,19 @@ const tableColOpt = [
   },
   {
     label: '手机号',
-    key: 'Phone'
+    key: 'Mobile'
   },
   {
     label: '注册时间',
-    key: 'RegisterTime',
-    sort: true
-  },
+    key: 'RegisterTime'
+  }, 
   {
     label: '最近一次阅读时间',
-    key: 'LastUpdateTime',
-    sort: true
+    key: 'ModifyTime'
   },
   {
     label: '阅读统计',
-    key: 'ReadCnt',
-    sort: true,
+    key: 'ReadTimes'
   },
 ]
 const userList = ref([])
@@ -53,17 +51,7 @@ async function getUserList() {
     PageSize: pageSize.value,
     CurrentIndex: page.value,
     Keywords: filterState.keyword,
-    ApplyStatus: filterState.ApplyStatus,
-    SellerDepartmentId: filterState.SellerDepartmentId,
-    SortParam: filterState.sortType,
-    SortType: filterState.sortVal,
     UserStatus: 1,
-    // CompanyId,
-    // StartTime, 
-    // EndTime,
-    // UserStatus,
-    // SortField: '',
-    // SortRule: '',
   })
   tableLoading.value = false
   if (res.Ret === 200) {
@@ -89,17 +77,6 @@ function handlePageChange(e) {
   page.value = e
   getUserList()
 }
-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
@@ -107,12 +84,39 @@ function handleFilterList() {
 }
 
 
+const showToFormalize=ref(false)
+const userInfo=ref({})
+function handleEditUser(e, type) {
+  if (type === 'toformalize') {
+    showToFormalize.value = true
+    userInfo.value = e
+  } else if(type==='delete'){
+    ElMessageBox.confirm(
+      '该操作会删除该用户所有相关信息,不可恢复,确认删除吗?',
+      '提示',
+      {
+        confirmButtonText: '确认',
+        cancelButtonText: '取消',
+        type: 'warning',
+      }
+    )
+      .then(() => {
+        handleDelUser(e)
+      })
+      .catch(() => {
+      })
+  }
+}
 
-function handleEditUser(e) {
-  router.push({
-    path: '/customer/userTransform',
-    query: {
-      id: e.UserId
+// 删除临时用户
+function handleDelUser(e) {
+  apiCustomerUser.userDelete({ UserId: e.UserId }).then((res) => {
+    if (res.Ret === 200) {
+      ElMessage({
+        type: 'success',
+        message: '删除成功!',
+      })
+      getUserList()
     }
   })
 }
@@ -120,94 +124,124 @@ function handleEditUser(e) {
 const showDetail=ref(false)
 const activeUserId=ref(0)
 const activeUserName=ref('')
-function handleShowDetail(e){
+function handleGoReadTimes(e){
+  if(e.ReadTimes < 1) return
   activeUserId.value=e.UserId
   activeUserName.value=e.RealName
   showDetail.value=true
 }
-
-
 </script>
 
 <template>
-  <div class="potential-user-list-page">
-    <div class="flex filter-wrap">
-      <el-input
-        placeholder="请输入机构名称/姓名/手机号"
-        v-model="filterState.keyword"
-        :prefix-icon="Search"
-        clearable
-        style="max-width: 359px;"
-        @input="handleFilterList"
-      />
-    </div>
-    <div class="userlist-wrap" style="margin-top: 20px">
-      <el-table
-        :data="userList"
-        border
-        highlight-current-row
-        element-loading-text="数据加载中..."
-        v-loading="tableLoading"
-        @sort-change="handleTableSort"
-      >
-        <el-table-column
-          v-for="column in tableColOpt"
-          :key="column.key"
-          :prop="column.key"
-          :label="column.label"
-          align="center"
-          :sortable="column.sort ? 'custom' : false"
+  <el-card>
+    <div class="potential-user-list-page">
+      <div class="flex filter-wrap">
+        <el-input
+          placeholder="请输入机构名称/姓名/手机号"
+          v-model="filterState.keyword"
+          :prefix-icon="Search"
+          clearable
+          style="max-width: 359px;"
+          @input="handleFilterList"
+        />
+      </div>
+      <div class="userlist-wrap" style="margin-top: 20px">
+        <el-table
+          :data="userList"
+          border
+          element-loading-text="数据加载中..."
+          v-loading="tableLoading"
+          :header-cell-style="{ background: '#EBEEF5' }"
         >
-          <template v-if="column.headerTips" #header>
-            <span>{{ column.label }}</span>
-            <el-tooltip
-              class="box-item"
-              effect="dark"
-              :content="column.headerTips"
-              placement="top"
-            >
-              <el-icon style="position: relative; top: 2px"
-                ><i-ep-QuestionFilled
-              /></el-icon>
-            </el-tooltip>
-          </template>
-          <template #default="{ row }">
-            <el-button v-if="column.key === 'ReadCnt'&&row.ReadCnt>0" link type="primary" @click="handleShowDetail(row)">{{row.ReadCnt}}</el-button>
-            <span v-else>{{ row[column.key] }}</span>
-          </template>
-        </el-table-column>
-        <el-table-column label="操作" align="center" width="200">
-          <template #default="{ row }">
-            <el-button type="primary" v-permission="'user:transform'" link @click="handleEditUser(row)"
-              >转正式</el-button
-            >
-            <el-button type="primary" v-permission="'user:transform'" link @click="handleEditUser(row)"
-              >删除</el-button
-            >
-          </template>
-        </el-table-column>
-      </el-table>
-      <el-pagination
-        background
-        layout="total,prev,pager,next,jumper"
-        :current-page="page"
-        :page-size="pageSize"
-        :total="totals"
-        @current-change="handlePageChange"
-        style="margin-top: 30px; justify-content: flex-end"
-      />
-    </div>
-  </div>
+          <el-table-column
+            v-for="column in tableColOpt"
+            :key="column.key"
+            :prop="column.key"
+            :label="column.label"
+            align="center"
+            :sortable="column.sort ? 'custom' : false"
+          >
+            <template v-if="column.headerTips" #header>
+              <span>{{ column.label }}</span>
+              <el-tooltip
+                class="box-item"
+                effect="dark"
+                :content="column.headerTips"
+                placement="top"
+              >
+                <el-icon style="position: relative; top: 2px"
+                  ><i-ep-QuestionFilled
+                /></el-icon>
+              </el-tooltip>
+            </template>
+            <template #default="{ row }">
+              <div class="name-box" v-if="column.key === 'RealName'">
+                <span class="name-tips" v-if="row.TrialExpired">试用过期</span>
+                <span>{{ row[column.key] || '-' }}</span>
+              </div>
+              <span v-else-if="column.key === 'ReadTimes'" :style="row.ReadTimes > 0 ?'color: #075EEE;' : ''" @click="handleGoReadTimes(row)">{{ row.ReadTimes}}</span>
+              <el-button v-else-if="column.key === 'ReadCnt'&&row.ReadCnt>0" link type="primary" @click="handleShowDetail(row)">{{row.ReadCnt}}</el-button>
+              <span v-else>{{ row[column.key] || '-' }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column label="操作" align="center" width="200">
+            <template #default="{ row }">
+              <el-button type="primary" v-permission="permissionBtn.customerManageBtn.trial_convert" link @click="handleEditUser(row, 'toformalize')"
+                >转正式</el-button
+              >
+              <el-button type="danger" v-permission="permissionBtn.customerManageBtn.trial_delete" link @click="handleEditUser(row, 'delete')"
+                >删除</el-button
+              >
+            </template>
+          </el-table-column>
+        </el-table>
+        <el-pagination
+          background
+          layout="total,prev,pager,next,jumper"
+          :current-page="page"
+          :page-size="pageSize"
+          :total="totals"
+          @current-change="handlePageChange"
+          class="pagination"
+        />
+      </div>
+    </div>  
+  </el-card>
   <UserStatisticDetail v-model:show="showDetail" :userId="activeUserId" :userName="activeUserName"/>
+  <ToFormalize v-model:show="showToFormalize" :data="userInfo" @success="handleFilterList"/>
 </template>
 
 <style lang="scss" scoped>
 .potential-user-list-page {
+  position: relative;
   width: 100%;
+  min-height: calc(100vh - 180px);
   overflow: hidden;
   .filter-wrap {
     flex-wrap: wrap;
     gap: 10px;
   }
+  .name-box {
+    position: relative;
+    .name-tips {
+      height: 25px;
+      border-radius: 5px;
+      padding: 0 10px;
+      background-color: red;
+      z-index: 9;
+      position: absolute;
+      top: 50%;
+      left: 10px;
+      transform: translateY(-50%);
+      color: #fff;
+      font-size: 12px;
+    }
+  }
+  .pagination {
+    position: absolute;
+    bottom: 30px;
+    right: 0;
+    justify-content: flex-end;
+  }
 }
 </style>

+ 10 - 30
src/views/customer/UserEdit.vue

@@ -62,6 +62,7 @@ const formState = reactive({
   isEnabled: false
 
 })
+const AuthStatus = ref(1)
 
 function getUserInfo() {
   if (!userId.value) return
@@ -70,10 +71,12 @@ function getUserInfo() {
   }).then(res => {
     if (res.Ret === 200) {
       const Detail = res.Data
+      console.log(Detail);
+      AuthStatus.value = Detail.AuthStatus
       formState.realName = Detail.RealName
       formState.areaCode = Detail.AreaCode
       formState.phone = Detail.Mobile
-      formState.sellerId = Detail.SellerDepartmentId||''
+      formState.sellerId = Detail.SellerId
       formState.isEnabled = Detail.Status ? true : false
     }
   })
@@ -90,30 +93,6 @@ async function getSellerDepartment() {
 }
 
 
-function handleToEdit() {
-  // 关闭提示
-  formState.isEnabled=true
-  ElMessageBox.close()
-  router.replace({
-    path: '/customer/userEdit',
-    query: {
-      id: userId.value
-    }
-  })
-}
-function checkAddContent(msg) {
-  return h('p', [
-    msg,
-    h(ElButton, {
-      type: 'primary',
-      link: true,
-      onClick: handleToEdit
-    }, '跳转查看')
-  ]);
-}
-
-
-
 async function handleSave(type) {
   await formRef.value.validate()
   let params = {
@@ -134,12 +113,13 @@ async function handleSave(type) {
     if (checkRes.Ret !== 200) return
     if (checkRes.Data.CheckResult !== 0) {
       // userId.value = checkRes.Data.UserId
-      ElMessageBox.confirm(checkAddContent(checkRes.Msg),'操作提示',{
+      // ElMessageBox.confirm(checkAddContent(checkRes.Data.Tips),'操作提示',{
+        ElMessageBox.confirm(checkRes.Data.Tips,'操作提示',{
         autofocus:false,
       }).then(()=>{
         // 确认则自动调用编辑保存一次用户信息
-        formState.isEnabled=true
-        handleSave()
+        // formState.isEnabled=true
+        // handleSave()
       }).catch(()=>{})
       return
     }
@@ -189,7 +169,7 @@ async function handleSave(type) {
             </el-select>
             <el-input
               v-model="showPhoneVal"
-              placeholder="手机号和邮箱至少填一个"
+              placeholder="输入手机号"
               style="width: 60%; margin-left: 5%"
             />
           </el-form-item>
@@ -217,7 +197,7 @@ async function handleSave(type) {
         </div>
         <div class="flex form-tr-box">
             <el-form-item label="权限">
-              可查看所有报告
+              {{ AuthStatus === 1 ? '可查看所有报告' : '无报告查看权限' }}
             </el-form-item>
           </div>
       </el-form>

+ 192 - 176
src/views/customer/UserList.vue

@@ -11,10 +11,10 @@ const router = useRouter()
 
 const filterState = reactive({
   keyword: '',
-  SellerDepartmentId: '',
+  sellerId: [],
   createTime: [],
-  sortType:'',
-  sortVal:''
+  SortField: 0,
+  SortRule: 0
 })
 
 const tableColOpt = computed( ()=>{
@@ -44,7 +44,8 @@ const tableColOpt = computed( ()=>{
       {
         label: '创建时间',
         key: 'CreateTime',
-        width: '200px'
+        width: '200px',
+        sort: true,
       },
     ]
   } else {
@@ -55,7 +56,7 @@ const tableColOpt = computed( ()=>{
       },
       {
         label: '手机号',
-        key: 'MobileHidden'
+        key: 'Mobile'
       },
       {
         label: '营业部',
@@ -72,10 +73,6 @@ const tableColOpt = computed( ()=>{
         sort: true,
         headerTips: '用户首次登录小程序的时间'
       },
-      {
-        label: '是否关注公众号',
-        key: 'IsSubscribed' 
-      },
       {
         label: '创建时间',
         key: 'CreateTime',
@@ -84,7 +81,7 @@ const tableColOpt = computed( ()=>{
       },
       { 
         label: '最近一次阅读时间',
-        key: 'ModifyTime',
+        key: 'LastReadTime',
         sort: true
       },
       {
@@ -111,16 +108,15 @@ async function getUserList() {
   const parames = {
     PageSize: pageSize.value,
     CurrentIndex: page.value,
-    SellerDepartmentId: filterState.SellerDepartmentId,
-    KeyWord: filterState.keyword,
-    CreateStartDate: filterState.createTime ? filterState.createTime[0] : '',
-    CreateEndDate: filterState.createTime ? filterState.createTime[1] : '',
-    SortParam:filterState.sortType,
-    SortType:filterState.sortVal,
+    SellerIds: filterState.sellerId.length > 0 ? filterState.sellerId.join(',') : '',
+    Keywords: filterState.keyword,
+    StartTime: filterState.createTime ? filterState.createTime[0] : '',
+    EndTime: filterState.createTime ? filterState.createTime[1] : '',
+    SortField:filterState.SortField,
+    SortRule:filterState.SortRule,
     UserStatus: 2
   }
-  console.log(radio.value === 1 ? 'companyPageList' : 'userList');
-  
+
   const res = radio.value === 1 ? await apiCustomerUser.companyPageList(parames) : await apiCustomerUser.userList(parames)
   tableLoading.value = false
   if (res.Ret === 200) {
@@ -149,17 +145,22 @@ async function getSellerDepartment() {
 }
 
 function handleTableSort(e) {
-  const { order, prop } = e//order:"descending",prop: "RegisterTime"
-  filterState.sortType=prop
-  if(!order){
-    filterState.sortVal=''
-  }else{
-    filterState.sortVal=order==='descending'?'desc':'asc'
-  }
-  
+
+  const sortFieldMap = {
+    CreateTime: 1,
+    RegisterTime: 2,
+    LastReadTime: 3,
+    ReadTimes: 4
+  };
+  // 设置排序字段
+  filterState.SortField = sortFieldMap[e.prop] || 0;
+  // 设置排序规则
+  filterState.SortRule = e.order ? (e.order === 'descending' ? 2 : 1) : 0;
   handleFilterList()
 }
 function handleFilterList() {
+  console.log('handleFilterList', filterState.sellerId);
+  
   page.value = 1
   getUserList()
 }
@@ -192,7 +193,8 @@ async function handleChangecompany(row) {
     CompanyId: row.CompanyId,
   })
   if (res.Ret !== 200) return
-  ElMessage.success('禁用成功')
+  console.log(row);
+  row.AuthStatus === 1 ? ElMessage.success('禁用成功') : ElMessage.success('启用成功')
   getUserList()
 }
 
@@ -200,7 +202,6 @@ async function handleChangecompany(row) {
 async function handleDisabledUser(row) {
   const res = await apiCustomerUser.setUserStatus({
     UserId: row.UserId,
-    IsEnabled: false
   })
   if (res.Ret !== 200) return
   ElMessage.success('禁用成功')
@@ -211,13 +212,11 @@ async function handleDisabledUser(row) {
 function handleEnableUser(row) {
   const params={
     UserId:row.UserId,
-    IsEnabled:true,
   }
   apiCustomerUser.setUserStatus(params).then(res=>{
     if(res.Ret===200){
       ElMessage.success('启用成功')
-      emits('success')
-      show.value=false
+      getUserList()
     }
   })
 }
@@ -225,7 +224,7 @@ function handleEnableUser(row) {
 // 删除机构或者用户
 function handleDelUserAndCompany(row) {
   ElMessageBox.confirm(
-    '该操作会删除该用户所有相关信息,不可恢复,确认删除吗?',
+    `删除后不可恢复,是否确认删除客户【${row.CompanyName || row.RealName}】所有信息?`,
     '提示'
   ).then(() => {
     if (radio.value === 2) {
@@ -314,6 +313,7 @@ const showDetail=ref(false)
 const activeUserId=ref(0)
 const activeUserName=ref('')
 function handleGoReadTimes(e){
+  if(e.ReadTimes < 1) return
   activeUserId.value=e.UserId
   activeUserName.value=e.RealName
   showDetail.value=true
@@ -321,164 +321,168 @@ function handleGoReadTimes(e){
 </script>
 
 <template>
-  <div class="customer-user-list-page">
-
-    <div class="select">
-      <el-radio-group v-model="radio" fill="#0052D9" size="large" @change="getUserList()">
-        <el-radio-button label="机构投资者" :value="1"></el-radio-button>
-        <el-radio-button label="个人投资者" :value="2"></el-radio-button>
-      </el-radio-group>
-    </div>
-
-    <div class="search-box">
-      <div class="flex filter-wrap">
-        <el-input
-          placeholder="请输入机构名称/联系人姓名/手机号"
-          v-model="filterState.keyword"
-          :prefix-icon="Search"
-          clearable
-          size="large"
-          style="max-width: 359px;float: right"
-          @input="handleFilterList"
-        />
-        <el-select
-          placeholder="请选择营业部"
-          v-model="filterState.SellerDepartmentId"
-          style="width: 165px"
-          clearable
-          size="large"
-          @change="handleFilterList"
-        >
-          <el-option v-for="(item, index) in departmentList" :key="index" :label="item.SysDepartmentName" :value="item.SysDepartmentId"></el-option>
-        </el-select>
-        <div style="width: 235px">
-          <el-date-picker
-            style="width: 235px"
-            v-model="filterState.createTime"
-            type="daterange"
-            range-separator="至"
-            start-placeholder="创建时间"
-            end-placeholder="创建时间"
-            value-format="YYYY-MM-DD"
+  <el-card>
+    <div class="customer-user-list-page">
+      <div class="select">
+        <el-radio-group v-model="radio" fill="#0052D9" size="large" @change="getUserList()">
+          <el-radio-button label="机构投资者" :value="1"></el-radio-button>
+          <el-radio-button label="个人投资者" :value="2"></el-radio-button>
+        </el-radio-group>
+      </div>
+      <div class="search-box">
+        <div class="flex filter-wrap">
+          <el-input
+            :placeholder="radio === 1 ? '请输入机构名称/联系人姓名/手机号' : '请输入投资者姓名/手机号'"
+            v-model="filterState.keyword"
+            :prefix-icon="Search"
             clearable
             size="large"
-            @change="handleFilterList"
+            style="max-width: 359px;float: right"
+            @input="handleFilterList"
           />
+          <div style="width: 235px">
+            <all-user-for-depart
+              style="width: 100%"
+              :props="{
+                emitPath: false,
+                multiple: true,
+              }"
+              :filterable="true"
+              onlySelectUser
+              :size="'large'"
+              v-model="filterState.sellerId"
+              placeholder="请选择营业部"
+              @change="handleFilterList"
+            />
+          </div>
+          <div style="width: 235px">
+            <el-date-picker
+              style="width: 235px"
+              v-model="filterState.createTime"
+              type="daterange"
+              range-separator="至"
+              start-placeholder="创建时间"
+              end-placeholder="创建时间"
+              value-format="YYYY-MM-DD"
+              clearable
+              size="large"
+              @change="handleFilterList"
+            />
+          </div>
+          
+        </div>
+        <div>
+          <el-button
+            type="primary"
+            :icon="Plus"
+            size="large"
+            @click="goAddUserPage"
+            v-permission="permissionBtn.customerManageBtn.customer_add"
+            >新增</el-button
+          >
         </div>
-        
       </div>
-      <div>
-        <el-button
-          type="primary"
-          :icon="Plus"
-          size="large"
-          @click="goAddUserPage"
-          v-permission="'user:add'"
-          >新增</el-button
+      <div class="userlist-wrap" style="margin-top: 30px">
+        <el-table
+          :data="userList"
+          border
+          element-loading-text="数据加载中..."
+          v-loading="tableLoading"
+          @sort-change="handleTableSort"
+          :header-cell-style="{ background: '#EBEEF5' }"
         >
+          <el-table-column
+            v-for="column in tableColOpt"
+            :key="column.key"
+            :prop="column.key"
+            :label="column.label"
+            :sortable="column.sort ? 'custom' : false"
+            :width="column.width"
+            align="center"
+          >
+            <template v-if="column.headerTips" #header>
+              <span>{{ column.label }}</span>
+              <el-tooltip
+                class="box-item"
+                effect="dark"
+                :content="column.headerTips"
+                placement="top"
+              >
+                <el-icon style="position: relative; top: 2px"
+                  ><i-ep-QuestionFilled
+                /></el-icon>
+              </el-tooltip>
+            </template>
+            <template #default="{ row }">
+              <span v-if="column.key === 'RealName' || column.key === 'CompanyName'" class="company-name" @click="handleGoDetail(row)">{{row.RealName || row.CompanyName}}</span>
+              <span v-else-if="column.key === 'ReadTimes'" :style="row.ReadTimes > 0 ?'color: #075EEE;' : ''" @click="handleGoReadTimes(row)">{{ row.ReadTimes}}</span>
+              <span v-else-if="column.key === 'IsRegistered'">{{
+                row.IsRegistered ? "是" : "否" || '-'
+              }}</span>
+              <span v-else-if="column.key === 'RegisterTime'">{{
+                formatTime(row.RegisterTime) || '-'
+              }}</span>
+              <span v-else-if="column.key === 'CreateTime'">{{
+                formatTime(row.CreateTime) || '-'
+              }}</span>
+              <span v-else>{{ row[column.key] || '-' }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column label="操作" width="200" align="center">
+            <template #default="{ row }">
+              <el-button
+                v-permission="permissionBtn.customerManageBtn.customer_edit"
+                type="primary"
+                link
+                @click.stop="handleEditUser(row)"
+                >编辑</el-button
+              >
+              <el-button
+                v-permission="permissionBtn.customerManageBtn.customer_permissionSettings"
+                type="danger"
+                link
+                v-if="row.AuthStatus === 1"
+                @click.stop="handleOpenAndClose(row,'close')"
+                >关闭权限</el-button
+              >
+              <el-button
+                v-permission="permissionBtn.customerManageBtn.customer_permissionSettings"
+                type="primary"
+                link
+                v-else
+                @click.stop="handleOpenAndClose(row,'open')"
+                >开启权限</el-button
+              >
+              <el-button
+                v-permission="permissionBtn.customerManageBtn.customer_delete"
+                type="danger"
+                link
+                @click.stop="handleDelUserAndCompany(row)"
+                >删除</el-button
+              >
+            </template>
+          </el-table-column>
+        </el-table>
+        <el-pagination
+          background
+          layout="total,prev,pager,next,jumper"
+          :current-page="page"
+          :page-size="pageSize"
+          :total="totals"
+          @current-change="handlePageChange"
+          class="pagination"
+        />
       </div>
     </div>
-      
-    <div class="userlist-wrap" style="margin-top: 30px">
-      <el-table
-        :data="userList"
-        border
-        element-loading-text="数据加载中..."
-        v-loading="tableLoading"
-        @sort-change="handleTableSort"
-      >
-        <el-table-column
-          v-for="column in tableColOpt"
-          :key="column.key"
-          :prop="column.key"
-          :label="column.label"
-          :sortable="column.sort ? 'custom' : false"
-          :width="column.width"
-          align="center"
-        >
-          <template v-if="column.headerTips" #header>
-            <span>{{ column.label }}</span>
-            <el-tooltip
-              class="box-item"
-              effect="dark"
-              :content="column.headerTips"
-              placement="top"
-            >
-              <el-icon style="position: relative; top: 2px"
-                ><i-ep-QuestionFilled
-              /></el-icon>
-            </el-tooltip>
-          </template>
-          <template #default="{ row }">
-            <span v-if="column.key === 'RealName' || column.key === 'CompanyName'" style="color: #075EEE;" @click="handleGoDetail(row)">{{row.RealName || row.CompanyName}}</span>
-            <span v-else-if="column.key === 'ReadTimes'" :style="row.ReadTimes > 0 ?'color: #075EEE;' : ''" @click="handleGoReadTimes(row)">{{ row.ReadTimes}}</span>
-            <span v-else-if="column.key === 'IsRegistered'">{{
-              row.IsRegistered ? "是" : "否" || '-'
-            }}</span>
-            <span v-else-if="column.key === 'IsSubscribed'">{{
-              row.IsSubscribed ? "是" : "否" || '-'
-            }}</span>
-            <span v-else-if="column.key === 'RegisterTime'">{{
-              formatTime(row.RegisterTime) || '-'
-            }}</span>
-            <span v-else-if="column.key === 'CreateTime'">{{
-              formatTime(row.CreateTime) || '-'
-            }}</span>
-            <span v-else>{{ row[column.key] || '-' }}</span>
-          </template>
-        </el-table-column>
-        <el-table-column label="操作" width="200" align="center">
-          <template #default="{ row }">
-            <el-button
-              v-permission="permissionBtn.customerManageBtn.customer_edit"
-              type="primary"
-              link
-              @click.stop="handleEditUser(row)"
-              >编辑</el-button
-            >
-            <el-button
-              v-permission="permissionBtn.customerManageBtn.customer_permissionSettings"
-              type="danger"
-              link
-              v-if="row.Status === 2"
-              @click.stop="handleOpenAndClose(row,'close')"
-              >关闭权限</el-button
-            >
-            <el-button
-              v-permission="permissionBtn.customerManageBtn.customer_permissionSettings"
-              type="primary"
-              link
-              v-else
-              @click.stop="handleOpenAndClose(row,'open')"
-              >开启权限</el-button
-            >
-            <el-button
-              v-permission="permissionBtn.customerManageBtn.customer_delete"
-              type="danger"
-              link
-              @click.stop="handleDelUserAndCompany(row)"
-              >删除</el-button
-            >
-          </template>
-        </el-table-column>
-      </el-table>
-      <el-pagination
-        background
-        layout="total,prev,pager,next,jumper"
-        :current-page="page"
-        :page-size="pageSize"
-        :total="totals"
-        @current-change="handlePageChange"
-        style="margin-top: 30px; justify-content: flex-end"
-      />
-    </div>
-  </div>
+  </el-card>
   <UserStatisticDetail v-model:show="showDetail" :userId="activeUserId" :userName="activeUserName"/>
 </template>
 
 <style lang="scss" scoped>
 .customer-user-list-page {
+  position: relative;
   width: 100%;
+  min-height: calc(100vh - 180px);
   overflow: hidden;
   .search-box {
     display: flex;
@@ -491,5 +495,17 @@ function handleGoReadTimes(e){
     flex-wrap: wrap;
     gap: 10px;
   }
+  .pagination {
+    position: absolute;
+    bottom: 30px;
+    right: 0;
+    justify-content: flex-end;
+  }
+}
+.company-name {
+  color: #075EEE;
+  white-space: nowrap;           /* 禁止换行 */
+  overflow: hidden;              /* 隐藏溢出内容 */
+  text-overflow: ellipsis;
 }
 </style>

+ 86 - 64
src/views/customer/UserSearch.vue

@@ -2,6 +2,7 @@
 import { Search } from '@element-plus/icons-vue'
 import { apiCustomerUser } from '@/api/customer'
 import { useRouter } from 'vue-router'
+import UserStatisticDetail from './reportStatistic/components/UserStatisticDetail.vue'
 
 const router = useRouter()
 
@@ -13,13 +14,9 @@ const searchFilters = reactive({
 
 const tableColumns = [
   { label: '用户姓名', key: 'RealName' },
-  { label: '手机号', key: 'Mobile' },
-  { label: '注册时间', key: 'CreateTime' },
-  { label: '最近一次阅读时间', key: 'ModifyTime' },
-  {
-    label: '阅读统计',
-    key: 'ReadTimes',
-  },
+  { label: '手机号', key: 'MobileHidden' },
+  { label: '注册时间', key: 'RegisterTime' },
+  { label: '最近一次阅读时间', key: 'LastReadTime' },
 ]
 
 const userList = ref([])
@@ -35,7 +32,7 @@ async function fetchUserList() {
     const res = await apiCustomerUser.userGlobalSearch({
       PageSize: pageSize.value,
       CurrentIndex: page.value,
-      KeyWord: searchFilters.keyword,
+      Keywords: searchFilters.keyword,
       SortParam: searchFilters.sortType,
       SortType: searchFilters.sortVal
     })
@@ -77,65 +74,90 @@ function handleFilterList() {
   }
 }
 
-function handleGoDetail(row) {
-  if (row.Status === 1) return
-  router.push({ path: '/customer/userDetail', query: { id: row.UserId } })
+
+
+const showDetail=ref(false)
+const activeUserId=ref(0)
+const activeUserName=ref('')
+function handleGoReadTimes(e){
+  if(e.ReadTimes < 1) return
+  activeUserId.value=e.UserId
+  activeUserName.value=e.RealName
+  showDetail.value=true
 }
 </script>
 
 <template>
-  <div class="user-search-page">
-    <el-input
-      placeholder="请输入机构名称/姓名/手机号"
-      v-model="searchFilters.keyword"
-      :prefix-icon="Search"
-      clearable
-      v-permission="permissionBtn.customerManageBtn.search_list"
-      style="max-width: 359px"
-      @input="handleFilterList"
-    />
-    <div class="userlist-wrap" style="margin-top: 20px">
-      <el-table
-        :data="userList"
-        border
-        highlight-current-row
-        element-loading-text="数据加载中..."
-        v-loading="tableLoading"
-        @row-click="handleGoDetail"
-      >
-        <el-table-column
-          v-for="column in tableColumns"
-          :key="column.key"
-          :prop="column.key"
-          :label="column.label"
-          align="center"
-          :sortable="column.sort ? 'custom' : false"
-        >
-          <template v-if="column.headerTips" #header>
-            <span>{{ column.label }}</span>
-            <el-tooltip
-              class="box-item"
-              effect="dark"
-              :content="column.headerTips"
-              placement="top"
-            >
-              <el-icon style="position: relative; top: 2px"><i-ep-QuestionFilled /></el-icon>
-            </el-tooltip>
-          </template>
-          <template #default="{ row }">
-            <span>{{ row[column.key] || '--' }}</span>
-          </template>
-        </el-table-column>
-      </el-table>
-      <el-pagination
-        background
-        layout="total,prev,pager,next,jumper"
-        :current-page="page"
-        :page-size="pageSize"
-        :total="totals"
-        @current-change="handlePageChange"
-        style="margin-top: 30px; justify-content: flex-end"
+  <el-card>
+    <div class="user-search-page">
+      <el-input
+        placeholder="请输入机构名称/姓名/手机号"
+        v-model="searchFilters.keyword"
+        :prefix-icon="Search"
+        clearable
+        v-permission="permissionBtn.customerManageBtn.search_list"
+        style="max-width: 359px"
+        @input="handleFilterList"
       />
+      <div class="userlist-wrap" style="margin-top: 20px">
+        <el-table
+          :data="userList"
+          border
+          highlight-current-row
+          element-loading-text="数据加载中..."
+          v-loading="tableLoading"
+          :header-cell-style="{ background: '#EBEEF5' }"
+        >
+          <el-table-column
+            v-for="column in tableColumns"
+            :key="column.key"
+            :prop="column.key"
+            :label="column.label"
+            align="center"
+            :sortable="column.sort ? 'custom' : false"
+          >
+            <template v-if="column.headerTips" #header>
+              <span>{{ column.label }}</span>
+              <el-tooltip
+                class="box-item"
+                effect="dark"
+                :content="column.headerTips"
+                placement="top"
+              >
+                <el-icon style="position: relative; top: 2px"><i-ep-QuestionFilled /></el-icon>
+              </el-tooltip>
+            </template>
+            <template #default="{ row }">
+              <span v-if="column.key === 'ReadTimes'" :style="row.ReadTimes > 0 ?'color: #075EEE;' : ''" @click="handleGoReadTimes(row)">{{ row.ReadTimes}}</span>
+              <span v-else>{{ row[column.key] || '-' }}</span>
+            </template>
+          </el-table-column>
+        </el-table>
+        <el-pagination
+          background
+          layout="total,prev,pager,next,jumper"
+          :current-page="page"
+          :page-size="pageSize"
+          :total="totals"
+          @current-change="handlePageChange"
+          class="pagination"
+        />
+      </div>
     </div>
-  </div>
-</template>
+  </el-card>
+  <UserStatisticDetail v-model:show="showDetail" :userId="activeUserId" :userName="activeUserName"/>
+</template>
+
+<style lang="scss" scoped>
+  .user-search-page {
+    position: relative;
+    width: 100%;
+    min-height: calc(100vh - 180px);
+    .pagination {
+      position: absolute;
+      bottom: 30px;
+      right: 0;
+      justify-content: flex-end;
+    }
+  }
+</style>

+ 202 - 0
src/views/customer/components/AddContact.vue

@@ -0,0 +1,202 @@
+<script setup>
+import { apiCustomerUser } from '@/api/customer'
+import { apiSystemCommon } from '@/api/system'
+import { isMobileNo } from '@/utils/common'
+import { ref, computed, watch } from 'vue';
+import { ElMessageBox } from 'element-plus'
+import { ElMessage } from 'element-plus';
+
+const show = defineModel('show', { type: Boolean, default: false });
+const props = defineProps({
+  data: Object,
+  contactTatle: String,
+  areaCodeOpts: Array,
+});
+const emits = defineEmits(["success"]);
+const formRef = ref(null);
+const formData = ref({
+  RealName: '',
+  CompanyId: null,
+  UserId: null,
+  areaCode: '86',
+  Mobile: '',
+});
+const formRules = {
+  RealName: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
+  Mobile: [
+    {
+      required: true,
+      validator: (rule, value, callback) => {
+        if (value && formData.value.areaCode === '86' && !isMobileNo(value || '')) {
+          callback(new Error('请输入正确的手机号格式'));
+        } else if (formData.value.areaCode !== '86' && isNaN(value?.trim())) {
+          callback(new Error('请输入正确的手机号格式'));
+        } else {
+          callback();
+        }
+      },
+      trigger: 'blur',
+    },
+  ],
+};
+
+const areaCodeOpts = ref([]);
+function getPhoneAreaCode() {
+  apiSystemCommon.phoneAreaCode().then(res => {
+    if (res.Ret === 200) {
+      areaCodeOpts.value = res.Data || [];
+    }
+  });
+}
+
+const showPhoneVal = computed({
+  get() {
+    return formData.value.Mobile;
+  },
+  set(value) {
+    formData.value.Mobile = value.replace(/ /g, '');
+  }
+});
+
+watch(() => show.value, (newval) => {
+  if (newval) {
+    formData.value.RealName = props.data.RealName;
+    formData.value.Mobile = props.data.Mobile;
+    formData.value.UserId = props.data.UserId;
+    formData.value.CompanyId = Number(props.data.CompanyId);
+    formRef.value ? formRef.value.clearValidate() : '';
+    getPhoneAreaCode();
+  }
+});
+
+async function handleSubmitForm() {
+  await formRef.value.validate();
+  const { areaCode, Mobile } = formData.value;
+
+  if (props.contactTatle === '添加联系人') {
+      // 新增用户时进行校验
+    const checkRes = await apiCustomerUser.userAddCheck({
+      AreaCode: areaCode,
+      Mobile,
+    })
+    if (checkRes.Ret !== 200) return
+    if (checkRes.Data.CheckResult === 2 || checkRes.Data.CheckResult === 3) {
+      ElMessageBox.alert(checkRes.Data.Tips,'操作提示',{
+        confirmButtonText: '知道了',
+        callback: action => {
+          if (action === 'confirm') {
+            // 用户点击了确认按钮
+          }
+        },
+      })
+    } else if (checkRes.Data.CheckResult === 1) {
+      ElMessageBox.confirm(checkRes.Data.Tips,'操作提示',{
+        autofocus:false,
+      }).then(()=>{
+        handleMoveUsersFetch(checkRes.Data.UserId)
+      }).catch(()=>{})
+    } else {
+      handleSubmitFetch()
+    }
+  } else {
+    handleSubmitFetch()
+  }
+}
+
+async function handleSubmitFetch() {
+  const { RealName, CompanyId, UserId, areaCode, Mobile } = formData.value;
+  const params = {
+    RealName,
+    CompanyId,
+    UserId,
+    AreaCode: areaCode,
+    Mobile: Mobile,
+  };
+  const res = props.contactTatle === '添加联系人' ? await apiCustomerUser.userAdd(params) : await apiCustomerUser.userEdit(params);
+  if (res.Ret !== 200) return;
+  props.contactTatle === '添加联系人' ? ElMessage.success('添加成功') : ElMessage.success('修改成功');
+  show.value = false;
+  emits("success");
+}
+
+async function handleMoveUsersFetch(UserId = null) {
+  const {CompanyId, RealName} = formData.value;
+  const params = {
+    CompanyId,
+    UserId,
+    RealName
+  };
+  const res = await apiCustomerUser.usersMove(params)
+  if (res.Ret !== 200) return;
+  ElMessage.success('添加成功');
+  show.value = false;
+  emits("success");
+}
+</script>
+
+<template>
+  <el-dialog
+    v-model="show"
+    :close-on-click-modal="false"
+    :modal-append-to-body="false"
+    width="30%"
+    :title="props.contactTatle || '添加联系人'"
+  >
+    <div class="dialog-content">
+      <el-form
+        label-width="80px"
+        :rules="formRules"
+        ref="formRef"
+        :model="formData"
+      >
+        <el-form-item label="姓名" prop="RealName">
+          <el-input
+            v-model="formData.RealName"
+            style="width: 100%"
+          ></el-input>
+        </el-form-item>
+        <el-form-item label="手机号" prop="Mobile">
+          <el-select
+            v-model="formData.areaCode"
+            placeholder="请选择"
+            style="width: 35%"
+          >
+            <el-option
+              v-for="item in areaCodeOpts"
+              :key="item.Value"
+              :label="item.Name"
+              :value="item.Value"
+            />
+          </el-select>
+          <el-input
+            v-model="showPhoneVal"
+            placeholder="输入手机号"
+            style="width: 60%; margin-left: 5%"
+          />
+        </el-form-item>
+      </el-form>
+    </div>
+    <template #footer>
+      <div class="btn-content">
+        <el-button
+          type="primary"
+          style="width: 80px; margin-right: 24px"
+          @click="handleSubmitForm"
+        >
+          保存
+        </el-button>
+        <el-button style="width: 80px" @click="show = false">取消</el-button>
+      </div>
+    </template>
+  </el-dialog>
+</template>
+
+<style scoped lang="scss">
+.dialog-content {
+  padding: 0 50px;
+}
+.btn-content {
+  text-align: center;
+  padding-bottom: 20px;
+}
+</style>

+ 73 - 104
src/views/customer/components/ContactTable.vue

@@ -3,28 +3,27 @@ import { apiCustomerUser } from '@/api/customer'
 import { useRouter, useRoute } from 'vue-router'
 import { ElMessage, ElMessageBox } from 'element-plus'
 import { computed } from 'vue'
+import MoveUser from './MoveUser.vue'
+import AddContact from './AddContact.vue'
+import UserStatisticDetail from '../reportStatistic/components/UserStatisticDetail.vue'
 
 const route = useRoute()
 const router = useRouter()
 
 const filterState = reactive({
-  sortType:'',
-  sortVal:''
+  SortField:'',
+  SortRule:''
 })
 
 const tableColOpt = computed( ()=>{
   return [
     {
-      label: '投资者姓名',
+      label: '联系人姓名',
       key: 'RealName'
     },
     {
       label: '手机号',
-      key: 'MobileHidden'
-    },
-    {
-      label: '营业部',
-      key: 'SellerName'
+      key: 'Mobile'
     },
     {
       label: '是否注册',
@@ -37,19 +36,9 @@ const tableColOpt = computed( ()=>{
       sort: true,
       headerTips: '用户首次登录小程序的时间'
     },
-    {
-      label: '是否关注公众号',
-      key: 'IsSubscribed' 
-    },
-    {
-      label: '创建时间',
-      key: 'CreateTime',
-      sort: true,
-      headerTips: '系统中新增该用户的时间'
-    },
     { 
       label: '最近一次阅读时间',
-      key: 'ModifyTime',
+      key: 'LastReadTime',
       sort: true
     },
     {
@@ -74,8 +63,8 @@ async function getUserList() {
     PageSize: pageSize.value,
     CurrentIndex: page.value,
     CompanyId: route.query.id,
-    SortParam:filterState.sortType,
-    SortType:filterState.sortVal,
+    SortField:filterState.SortField,
+    SortRule:filterState.SortRule,
     UserStatus: 2
   }
   const res = await apiCustomerUser.userList(parames)
@@ -102,48 +91,31 @@ async function getSellerDepartment() {
 }
 
 function handleTableSort(e) {
-  const { order, prop } = e//order:"descending",prop: "RegisterTime"
-  filterState.sortType=prop
-  if(!order){
-    filterState.sortVal=''
-  }else{
-    filterState.sortVal=order==='descending'?'desc':'asc'
-  }
-  
-  handleFilterList()
+
+const sortFieldMap = {
+  CreateTime: 1,
+  RegisterTime: 2,
+  LastReadTime: 3,
+  ReadTimes: 4
+};
+// 设置排序字段
+filterState.SortField = sortFieldMap[e.prop] || 0;
+// 设置排序规则
+filterState.SortRule = e.order ? (e.order === 'descending' ? 2 : 1) : 0;
+handleFilterList()
 }
 function handleFilterList() {
   page.value = 1
   getUserList()
 }
 
-function handleEditUser(e) {
-  const link=router.resolve({
-    path: '/customer/userEdit',
-    query: {
-      id: e.UserId
-    }
-  }).href
-  window.open(link,'__blank')
-}
-
-// 禁用用户
-async function handleDisabledUser(row) {
-  const res = await apiCustomerUser.setUserStatus({
-    UserId: row.UserId,
-    IsEnabled: false
-  })
-  if (res.Ret !== 200) return
-  ElMessage.success('禁用成功')
-  getUserList()
-}
 
-// 启用用户
-const showEnableUserPop = ref(false)
-const enableUserId = ref(0)
-function handleEnableUser(row) {
-  enableUserId.value = row.UserId
-  showEnableUserPop.value = true
+// 移动用户
+const showMove=ref(false)
+const activeUserId=ref(0)
+function handleMove(e){
+  activeUserId.value=e.UserId
+  showMove.value=true
 }
 
 // 删除用户
@@ -164,42 +136,47 @@ function handleDelUser(row) {
   }).catch(() => { })
 }
 
-// 跳转详情
-function handleGoDetail(e){
-  let link = ''
-  if (radio.value === 2) {
-    link = router.resolve({
-      path:'/customer/userDetail',
-      query:{
-        id:e.UserId
-      }
-    }).href
+const userInfo = ref({})
+const contactTatle = ref('添加联系人')
+const showAddContact = ref(false)
+function handleAddContact(row, type = 'edit') {
+  if (type === 'add') {
+    contactTatle.value = '添加联系人'
   } else {
-    link = router.resolve({
-      path:'/customer/companyDetail',
-      query:{
-        id:e.UserId
-      }
-    }).href
+    contactTatle.value = '编辑联系人'
   }
+  userInfo.value = { CompanyId: route.query.id, ...row }
+  showAddContact.value = true
+}
 
-  window.open(link,'__blank')
+const showDetail=ref(false)
+const readTimesUserId=ref(0)
+const activeUserName=ref('')
+function handleGoReadTimes(e){
+  if(e.ReadTimes < 1) return
+  readTimesUserId.value=e.UserId
+  activeUserName.value=e.RealName
+  showDetail.value=true
 }
 
 </script>
 
 <template>
   <div class="customer-user-list-page">
+    <el-button
+      v-permission="permissionBtn.customerManageBtn.contact_add"
+      type="primary" color="#0052D9" style="width: 120px; margin-top: 10px;" @click="handleAddContact({}, 'add')"
+      >添加联系人</el-button
+    >
     <div class="userlist-wrap" style="margin-top: 30px">
       <el-table
         :data="userList"
         border
-        
         highlight-current-row
         element-loading-text="数据加载中..."
         v-loading="tableLoading"
         @sort-change="handleTableSort"
-        @row-click="handleGoDetail"
+        :header-cell-style="{ background: '#EBEEF5' }"
       >
         <el-table-column
           v-for="column in tableColOpt"
@@ -227,50 +204,31 @@ function handleGoDetail(e){
             <span v-if="column.key === 'IsRegistered'">{{
               row.IsRegistered ? "是" : "否"
             }}</span>
-            <span v-else-if="column.key === 'IsSubscribed'">{{
-              row.IsSubscribed ? "是" : "否"
-            }}</span>
-            <span v-else-if="column.key === 'ValidStartTime'"
-              >{{ formatTime(row.ValidStartTime, "YYYY-MM-DD") }}~{{
-                formatTime(row.ValidEndTime, "YYYY-MM-DD")
-              }}</span
-            >
             <span v-else-if="column.key === 'RegisterTime'">{{
-              formatTime(row.RegisterTime)
+              formatTime(row.RegisterTime)  || '-'
             }}</span>
-            <span v-else-if="column.key === 'CreateTime'">{{
-              formatTime(row.CreateTime)
-            }}</span>
-            <span v-else>{{ row[column.key] }}</span>
+            <span v-else-if="column.key === 'ReadTimes'"  :style="row.ReadTimes > 0 ?'color: #075EEE;' : ''" @click="handleGoReadTimes(row)">{{ row.ReadTimes}}</span>
+            <span v-else>{{ row[column.key] || '-' }}</span>
           </template>
         </el-table-column>
         <el-table-column label="操作" width="200" align="center">
           <template #default="{ row }">
             <el-button
-              v-permission="permissionBtn.customerManageBtn.customer_edit"
+              v-permission="permissionBtn.customerManageBtn.contact_add"
               type="primary"
               link
-              @click.stop="handleEditUser(row)"
+              @click.stop="handleAddContact(row, 'edit')"
               >编辑</el-button
             >
             <el-button
-              v-permission="permissionBtn.customerManageBtn.customer_permissionSettings"
-              type="danger"
-              link
-              v-if="row.Status === 2"
-              @click.stop="handleDisabledUser(row)"
-              >关闭权限</el-button
-            >
-            <el-button
-              v-permission="permissionBtn.customerManageBtn.customer_permissionSettings"
+              v-permission="permissionBtn.customerManageBtn.contact_move"
               type="primary"
               link
-              v-else
-              @click.stop="handleEnableUser(row)"
-              >开启权限</el-button
+              @click.stop="handleMove(row)"
+              >移动</el-button
             >
             <el-button
-              v-permission="permissionBtn.customerManageBtn.customer_delete"
+              v-permission="permissionBtn.customerManageBtn.contact_delete"
               type="danger"
               link
               @click.stop="handleDelUser(row)"
@@ -290,7 +248,18 @@ function handleGoDetail(e){
       />
     </div>
   </div>
-
+  <MoveUser
+    v-model:show="showMove"
+    :userId="activeUserId"
+    @success="getUserList"
+  />
+  <AddContact
+    v-model:show="showAddContact"
+    :data="userInfo"
+    @success="getUserList"
+    :contactTatle="contactTatle"
+  />
+  <UserStatisticDetail v-model:show="showDetail" :userId="readTimesUserId" :userName="activeUserName"/>
 </template>
 
 <style lang="scss" scoped>

+ 109 - 0
src/views/customer/components/MoveUser.vue

@@ -0,0 +1,109 @@
+<script setup>
+import { apiCustomerUser } from '@/api/customer'
+
+const show = defineModel('show', { type: Boolean, default: false })
+const props = defineProps({
+  userId: { type: Number, default: 0 },
+})
+const emits = defineEmits(["success"])
+const formRef = ref(null)
+const formData = ref({
+  CompanyId: null,
+  UserId: null,
+})
+const formRules = {
+  CompanyId: [
+    { required: true, message: '请选择投资者机构', trigger: 'change' },
+  ],
+}
+
+// 获取部门数据
+const departArr = ref([])
+async function getDepartList() {
+  const res = await apiCustomerUser.companySearch()
+  if (res.Ret !== 200) return
+  const list = res.Data || []
+  departArr.value = list
+}
+
+watch(() => show.value, (newval) => {
+  if (newval) {
+    formData.value.UserId = props.userId
+    formRef.value ? formRef.value.clearValidate() : '';
+    getDepartList()
+  }
+})
+
+async function handleSubmitForm() {
+  await formRef.value.validate()
+  
+  const { CompanyId, UserId } = formData.value
+
+  const res=await apiCustomerUser.usersMove({
+    CompanyId,
+    UserId,
+  })
+  if(res.Ret!==200) return
+  ElMessage.success('操作成功')
+  show.value=false
+  emits("success")
+}
+</script>
+
+<template>
+  <el-dialog
+    v-model="show"
+    :close-on-click-modal="false"
+    :modal-append-to-body="false"
+    width="30%"
+    title="移动"
+  >
+    <div class="dialog-content">
+      <el-form
+      label-width="80px"
+      :rules="formRules"
+      ref="formRef"
+      :model="formData"
+      >
+      <el-form-item label="移动到" prop="CompanyId">
+          <el-select
+            v-model="formData.CompanyId"
+            placeholder="请选择投资者机构"
+            size="large"
+            style="width: 100%"
+            filterable
+            no-match-text="暂无该机构,请前往客户列表添加投资者机构!"
+          >
+            <el-option
+              v-for="item in departArr"
+              :key="item.CompanyId"
+              :label="item.CompanyName"
+              :value="item.CompanyId"
+            />
+          </el-select>
+        </el-form-item>
+      </el-form>
+    </div>
+    <template #footer>
+      <div class="btn-content">
+        <el-button
+          type="primary"
+          style="width: 80px; margin-right: 24px"
+          @click="handleSubmitForm"
+          >保存</el-button
+        >
+        <el-button style="width: 80px" @click="show = false">取消</el-button>
+      </div>
+    </template>
+  </el-dialog>
+</template>
+
+<style scoped lang="scss">
+.dialog-content {
+  padding: 0 50px;
+}
+.btn-content {
+  text-align: center;
+  padding-bottom: 20px;
+}
+</style>

+ 162 - 0
src/views/customer/components/ToFormalize.vue

@@ -0,0 +1,162 @@
+<script setup>
+import { apiCustomerUser } from '@/api/customer'
+
+const show = defineModel('show', { type: Boolean, default: false })
+const props = defineProps({
+  data: Object,
+})
+const emits = defineEmits(["success"])
+const formRef = ref()
+const formData = ref({
+  RealName: '',
+  SellerId: null,
+  CompanyId: null,
+  UserId: null,
+  UserType: null
+})
+const formRules = {
+  RealName: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
+  SellerId: [{ required: true, message: '请选择营业部', trigger: 'change' }],
+  UserType: [{ required: true, message: '请选择客户类型', trigger: 'blur' }],
+  CompanyId: [{ required: true, message: '请选择机构', trigger: 'blur' }],
+}
+
+const options = ref([
+  { label: '个人投资者', value: 1 },
+  { label: '机构投资者', value: 2 },
+])
+
+
+// 获取部门数据
+const departArr = ref([])
+async function getDepartList() {
+  const res = await apiCustomerUser.companySearch()
+  if (res.Ret !== 200) return
+  const list = res.Data || []
+  departArr.value = list
+}
+
+
+watch(() => show.value, (newval) => {
+  if (newval) {
+    formData.value.RealName = props.data.RealName
+    formData.value.Mobile = props.data.Mobile
+    formData.value.UserId = props.data.UserId
+    formRef.value ? formRef.value.clearValidate() : '';
+    getDepartList()
+  }
+})
+
+async function handleSubmitForm() {
+  await formRef.value.validate()
+  
+  const {RealName, SellerId, CompanyId, UserId, UserType} = formData.value
+
+  const res=await apiCustomerUser.usersTransFormal({
+    RealName,
+    SellerId,
+    CompanyId,
+    UserId,
+    UserType
+  })
+  if(res.Ret!==200) return
+  ElMessage.success('操作成功')
+  show.value=false
+  emits("success")
+}
+</script>
+
+<template>
+  <el-dialog
+    v-model="show"
+    :close-on-click-modal="false"
+    :modal-append-to-body="false"
+    width="30%"
+    title="转正式"
+  >
+    <div class="dialog-content">
+      <el-form
+      label-width="80px"
+      :rules="formRules"
+      ref="formRef"
+      :model="formData"
+      >
+        <el-form-item label="姓名" prop="RealName">
+          <el-input
+            v-model="formData.RealName"
+            style="width: 100%"
+          ></el-input>
+        </el-form-item>
+        <el-form-item label="手机号">
+          <span>{{ formData.Mobile }}</span>
+        </el-form-item>
+        <el-form-item label="客户类型" prop="UserType">
+          <el-select
+            v-model="formData.UserType"
+            placeholder="请选择客户类型"
+            size="large"
+            style="width: 100%"
+          >
+            <el-option
+              v-for="item in options"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="所属机构" prop="CompanyId" v-if="formData.UserType === 2">
+          <el-select
+            v-model="formData.CompanyId"
+            placeholder="请选择客户类型"
+            size="large"
+            filterable
+            no-match-text="暂无该机构,请前往客户列表添加投资者机构!"
+            style="width: 100%"
+          >
+            <el-option
+              v-for="item in departArr"
+              :key="item.CompanyId"
+              :label="item.CompanyName"
+              :value="item.CompanyId"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="营业部" prop="SellerId" v-if="formData.UserType === 1">
+          <all-user-for-depart
+            style="width: 100%"
+            :props="{
+             emitPath: false,
+            }"
+            :filterable="true"
+            onlySelectUser
+            :size="'large'"
+            v-model="formData.SellerId"
+            placeholder="请选择营业部"
+          />
+        </el-form-item>
+      </el-form>
+    </div>
+    <template #footer>
+      <div class="btn-content">
+        <el-button
+          type="primary"
+          style="width: 80px; margin-right: 24px"
+          @click="handleSubmitForm"
+          >保存</el-button
+        >
+        <el-button style="width: 80px" @click="show = false">取消</el-button>
+      </div>
+    </template>
+  </el-dialog>
+</template>
+
+<style scoped lang="scss">
+.dialog-content {
+  padding: 0 50px;
+}
+.btn-content {
+  text-align: center;
+  padding-bottom: 20px;
+}
+</style>

+ 0 - 95
src/views/customer/reportStatistic/Chart.vue

@@ -1,95 +0,0 @@
-<script setup>
-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({
-  type: '',
-  keyword: ''
-})
-
-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
-    })
-    searchLoading.value = false
-    if (res.Ret === 200) {
-      const arr = res.Data.List || []
-      options.value = arr
-    } else {
-      options.value = []
-    }
-  }
-}
-
-
-
-</script>
-
-<template>
-  <div class="user-report-statistic-chart-page">
-    <div class="flex filter-wrap">
-      <el-button
-        type="primary"
-        :icon="Switch"
-        link
-        @click="emits('change', 'chart')"
-        >数据表</el-button
-      >
-      <el-select
-        v-model="userId"
-        filterable
-        remote
-        placeholder="姓名/手机号/邮箱"
-        :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">
-        <LineChart :userId="userId"/>
-      </div>
-      <div class="bg-white item-wrap">
-        <PieChart :userId="userId"/>
-      </div>
-    </div>
-  </div>
-</template>
-
-<style lang="scss" scoped>
-.filter-wrap {
-  gap: 10px;
-}
-.content-wrap {
-  display: flex;
-  gap: 0 30px;
-  .item-wrap {
-    margin-top: 20px;
-    flex: 1;
-  }
-}
-</style>

+ 0 - 15
src/views/customer/reportStatistic/Index.vue

@@ -1,15 +0,0 @@
-<script setup>
-import List from './List.vue'
-import Chart from './Chart.vue'
-
-const activeComp=shallowRef(List)
-
-function changeComp(type){
-  activeComp.value=type==='list'?Chart:List
-}
-
-</script>
-
-<template>
-  <component :is="activeComp" @change="changeComp"></component>
-</template>

+ 0 - 258
src/views/customer/reportStatistic/List.vue

@@ -1,258 +0,0 @@
-<script setup>
-import { Search,Switch } from '@element-plus/icons-vue'
-import { apiCustomerStatistic } from '@/api/customer'
-import UserStatisticDetail from './components/UserStatisticDetail.vue'
-import { useRouter } from 'vue-router'
-import { apiCustomerUser } from '@/api/customer'
-
-const router=useRouter()
-
-const emits=defineEmits(['change'])
-
-const filterState=reactive({
-  keyword: '',
-  SellerDepartmentId: '',
-  status: '',
-  register: '',
-  subscribe: '',
-  regsiterTime: [],
-  createTime: []
-})
-
-const tableColOpt = [
-  {
-    label: '姓名',
-    key: 'RealName'
-  },
-  {
-    label: '手机号',
-    key: 'Phone'
-  },
-  {
-    label: '邮箱',
-    key: 'Email'
-  },
-  {
-    label: '营业部',
-    key: 'SellerDepartmentName'
-  },
-  {
-    label: '用户状态',
-    key: 'Status'
-  },
-  {
-    label: '最近一次阅读时间',
-    key: 'LastUpdateTime',
-    sort: true
-  },
-  {
-    label: '累计阅读次数',
-    key: 'ReadCnt',
-    sort: true
-  }
-]
-const userList = ref([])
-const departmentList = ref([]) // 营业部列表
-const page = ref(1)
-const pageSize = ref(10)
-const tableLoading = ref(false)
-const totals = ref(0)
-async function getUserList() {
-  tableLoading.value = true
-  const res = await apiCustomerStatistic.readRecordList({
-    PageSize: pageSize.value,
-    CurrentIndex: page.value,
-    KeyWord: filterState.keyword,
-    SellerDepartmentId: filterState.SellerDepartmentId,
-    Status: filterState.status,
-    IsRegistered: filterState.register,
-    IsSubscribed: filterState.subscribe,
-    RegisterStartDate: filterState.regsiterTime ? filterState.regsiterTime[0] : '',
-    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) {
-    userList.value = res.Data.List || []
-    totals.value = res.Data.Paging.Totals
-  }
-}
-getUserList()
-getSellerDepartment()
-function handlePageChange(e) {
-  page.value = e
-  getUserList()
-}
-async function getSellerDepartment() {
-  tableLoading.value = true
-  const res = await apiCustomerUser.sellerDepartmentList()
-  tableLoading.value = false
-  if (res.Ret === 200) {
-    departmentList.value = res.Data || []
-  }
-}
-
-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()
-}
-
-
-const showDetail=ref(false)
-const activeUserId=ref(0)
-const activeUserName=ref('')
-function handleShowDetail(e){
-  activeUserId.value=e.UserId
-  activeUserName.value=e.RealName
-  showDetail.value=true
-}
-
-// 跳转详情
-function handleGoDetail(e){
-  const link=router.resolve({
-    path:'/customer/userDetail',
-    query:{
-      id:e.UserId
-    }
-  }).href
-  window.open(link,'__blank')
-}
-
-</script>
-
-<template>
-  <div class="user-report-statistic-page">
-    <div class="flex filter-wrap">
-      <el-button type="primary" :icon="Switch" link @click="emits('change','list')">统计图</el-button>
-      <el-select
-        placeholder="请选择营业部"
-        v-model="filterState.SellerDepartmentId"
-        style="width: 165px"
-        clearable
-        @change="handleFilterList"
-      >
-        <el-option v-for="(item, index) in departmentList" :key="index" :label="item.SysDepartmentName" :value="item.SysDepartmentId"></el-option>
-      </el-select>
-      <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>
-      </el-select>
-      <el-select
-        placeholder="注册状态"
-        v-model="filterState.register"
-        style="width: 165px"
-        @change="handleFilterList"
-      >
-        <el-option label="是" value="是"></el-option>
-        <el-option label="否" value="否"></el-option>
-      </el-select>
-      <el-select
-        placeholder="是否关注公众号"
-        v-model="filterState.register"
-        style="width: 165px"
-        @change="handleFilterList"
-      >
-        <el-option label="是" value="是"></el-option>
-        <el-option label="否" value="否"></el-option>
-      </el-select>
-      <span style="width: 235px">
-        <el-date-picker
-          style="width: 235px"
-          v-model="filterState.regsiterTime"
-          type="daterange"
-          range-separator="至"
-          start-placeholder="注册时间"
-          end-placeholder="注册时间"
-          value-format="YYYY-MM-DD"
-          @change="handleFilterList"
-        />
-      </span>
-      <span style="width: 235px">
-        <el-date-picker
-          style="width: 235px"
-          v-model="filterState.createTime"
-          type="daterange"
-          range-separator="至"
-          start-placeholder="创建时间"
-          end-placeholder="创建时间"
-          value-format="YYYY-MM-DD"
-          @change="handleFilterList"
-        />
-      </span>
-      <el-input
-        placeholder="姓名/手机号/邮箱"
-        v-model="filterState.keyword"
-        :prefix-icon="Search"
-        clearable
-        style="max-width: 359px;margin-left:auto"
-        @input="handleFilterList"
-      />
-    </div>
-    <div class="userlist-wrap" style="margin-top: 20px">
-      <el-table
-        :data="userList"
-        border
-        stripe
-        highlight-current-row
-        element-loading-text="数据加载中..."
-        v-loading="tableLoading"
-        @sort-change="handleTableSort"
-        @row-click="handleGoDetail"
-      >
-        <el-table-column
-          v-for="column in tableColOpt"
-          :key="column.key"
-          :prop="column.key"
-          :label="column.label"
-          :sortable="column.sort ? 'custom' : false"
-        >
-          <template #default="{ row }">
-            <span
-              v-if="column.key === 'Status'"
-              :style="{ color: !row.Status ? '#f00' : '' }"
-              >{{ row.Status ? "启用" : "禁用" }}</span
-            >
-            <el-button v-else-if="column.key === 'ReadCnt'&&row.ReadCnt>0" link type="primary" @click.stop="handleShowDetail(row)">{{row.ReadCnt}}</el-button>
-            <span v-else>{{ row[column.key] }}</span>
-          </template>
-        </el-table-column>
-      </el-table>
-      <el-pagination
-        background
-        layout="total,prev,pager,next"
-        :current-page="page"
-        :page-size="pageSize"
-        :total="totals"
-        @current-change="handlePageChange"
-        style="margin-top: 30px;justify-content: flex-end;"
-      />
-    </div>
-  </div>
-  <UserStatisticDetail v-model:show="showDetail" :userId="activeUserId" :userName="activeUserName"/>
-</template>
-
-<style lang="scss" scoped>
-.filter-wrap{
-  gap: 10px;
-  flex-wrap: wrap;
-}
-</style>

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

@@ -1,103 +0,0 @@
-<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>

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

@@ -1,55 +0,0 @@
-<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>

+ 10 - 16
src/views/customer/reportStatistic/components/UserStatisticDetail.vue

@@ -27,11 +27,13 @@ const tableColOpt = [
   },
   {
     label: '阅读时间',
-    key: 'CreateTime'
+    key: 'CreateTime',
+    width: 170,
   },
   {
     label: '停留时间',
-    key: 'ReadDuration'
+    key: 'ReadDuration',
+    width: 100,
   },
 ]
 const list = ref([])
@@ -72,15 +74,6 @@ watch(() => show.value, (n) => {
   }
 })
 
-function formatClassifyName(e){
-  if(e.ClassifyName3){
-    return `${e.ClassifyName1}/${e.ClassifyName2}/${e.ClassifyName3}`
-  }else if(e.ClassifyName2){
-    return `${e.ClassifyName1}/${e.ClassifyName2}`
-  }else{
-    return e.ClassifyName1
-  }
-}
 
 </script>
 
@@ -106,20 +99,21 @@ function formatClassifyName(e){
         highlight-current-row
         element-loading-text="数据加载中..."
         v-loading="tableLoading"
-        height="500px"
+        height="400px"
       >
         <el-table-column
           v-for="column in tableColOpt"
           :key="column.key"
           :prop="column.key"
           :label="column.label"
+          :width="column.width"
         >
           <template #default="{ row }">
-            <span v-if="column.key === 'CreateTime'">{{
-              formatTime(row.CreateTime)
+            <span v-if="column.key === 'ClassifyNameThird'">{{
+              row.ClassifyNameFirst + '/' + row.ClassifyNameSecond + '/' + row.ClassifyNameThird
             }}</span>
-            <span v-else-if="column.key === 'ClassifyName1'">{{
-              formatClassifyName(row)
+            <span v-else-if="column.key === 'CreateTime'">{{
+              formatTime(row.CreateTime)
             }}</span>
             <span v-else>{{ row[column.key] }}</span>
           </template>

+ 0 - 140
src/views/customer/reportStatistic/utils/chartRender.js

@@ -1,140 +0,0 @@
-import Highcharts from "highcharts/highstock";
-
-const chartBaseConfig = {
-  title: {
-    text: "",
-  },
-  accessibility: {
-    enabled: false,
-  },
-  chart: {
-    style: {
-      fontSize: 16,
-    },
-  },
-  credits: {
-    enabled: false,
-  },
-  legend: {
-    align: "center",
-    verticalAlign: "top",
-  },
-};
-
-export function lineChartRender(data) {
-  let options = {
-    // colors: ["#436DFF", "#25DEDB"],
-    plotOptions: {
-      series: {
-        marker: {
-          fillColor: "#FFFFFF",
-          lineWidth: 2,
-          lineColor: null,
-        },
-      },
-    },
-    xAxis: {
-      categories: data.time,
-      labels: {
-        style: {
-          color: "#999",
-        },
-      },
-      tickWidth: 1,
-      tickLength: 5,
-      lineColor:'#bfbfbf',
-      tickColor:'#bfbfbf',
-    },
-    yAxis: [
-      {
-        gridLineWidth: 0,
-        lineWidth: 1,
-        lineColor:'#bfbfbf',
-        tickColor:'#bfbfbf',
-        tickWidth: 1,
-        tickLength: 5,
-        labels: {
-          style: {
-            color: "#999",
-          },
-        },
-        title: {
-          text: "次",
-          align: "high",
-          style: {
-            color: "#999",
-          },
-          y: -10,
-          offset: 0,
-          rotation: 0,
-        },
-      },
-    ],
-    tooltip: {
-      format: '<span style="font-size: 0.8em">{key}</span><br/>'+
-      '<span style="color:{color}">\u25CF</span> ' +
-      '{series.name}: <b>{y}</b><br/>',
-    },
-    series: [
-      {
-        data: data.value,
-        type: "line",
-        name: "阅读量",
-        yAxis: 0,
-      },
-    ],
-    ...chartBaseConfig,
-  };
-  Highcharts.chart("chartBox1", options);
-}
-
-export function pieChartRender(data) {
-  let options = {
-    ...chartBaseConfig,
-    // colors: ["#59F7CA", "#62F2F8", "#F2B949", "#FF8838", "#FDFDFD", "#4A7CE9"],
-    credits: {
-      enabled: false,
-    },
-    chart: {
-      ...chartBaseConfig.chart,
-    },
-    title: {
-      text: "",
-    },
-    legend: {
-      ...chartBaseConfig.legend,
-      align: "right",
-      verticalAlign: "top",
-      layout: "vertical",
-      x: 0,
-      y: 0,
-    },
-    tooltip: {
-      shared: false,
-      pointFormat: '<span>{point.y}次:{point.Percent}%<br/>'
-    },
-    plotOptions: {
-      pie: {
-        allowPointSelect: true,
-        cursor: "pointer",
-        dataLabels: {
-          enabled: true,
-          distance: -10,
-          format: "",
-        },
-        showInLegend: true,
-      },
-    },
-    series: [
-      {
-        type: "pie",
-        innerSize: "80%",
-        name: "",
-        colorByPoint: true,
-        borderRadius: 0,
-        data: data,
-      },
-    ],
-  };
-  Highcharts.chart("chartBox2", options);
-}

+ 2 - 2
src/views/system/AuthSet.vue

@@ -64,12 +64,12 @@ function handleSave(){
   }
   const cArr=Array.from(new Set([...keys,...halfKeys]))
   apiSystemRole.roleAuthSet({
-    SysRoleId:roleId.value,
+    SysRoleId:parseInt(roleId.value),
     SysMenuIds:cArr,
     HalfMenuIds:halfKeys
   }).then(res=>{
     if(res.Ret!==200) return
-    ElMessage.success('保存成功')
+    ElMessage.success('权限设置成功')
     handlRoleChange()
   })
 }

+ 1 - 1
src/views/system/RoleList.vue

@@ -82,7 +82,7 @@ function handleRoleAdd(params) {
   <div class="system-rolelist-page">
     <el-card>
       <div class="shadow-box top-box">
-        <el-button type="primary" @click="handleEdit('')" v-permission="'role:add'">添加角色</el-button>
+        <el-button type="primary" @click="handleEdit('')" v-permission="permissionBtn.systemManageBtn.role_convert">添加角色</el-button>
       </div>
     </el-card>
     <div class="shadow-box content-box">

+ 4 - 4
src/views/system/userList/Index.vue

@@ -183,27 +183,27 @@ async function changeStatus(e) {
               <template #default="{ row }">
                 <el-button
                   type="primary"
-                  v-permission="'sysUser:edit'"
+                  v-permission="permissionBtn.systemManageBtn.user_add"
                   link
                   @click.stop="handleAddUser(row)"
                   >编辑</el-button
                 >
                 <el-button
-                  v-permission="'sysUser:resetPassword'"
+                  v-permission="permissionBtn.systemManageBtn.user_delete"
                   type="primary"
                   link
                   @click.stop="openResetPassDialog(row)"
                   >重置密码</el-button
                 >
                 <el-button
-                  v-permission="'sysUser:move'"
+                  v-permission="permissionBtn.systemManageBtn.user_edit"
                   type="primary"
                   link
                   @click.stop="openMoveDepartDialog(row)"
                   >移动分组</el-button
                 >
                 <el-button
-                  v-permission="'sysUser:editEnabled'"
+                  v-permission="permissionBtn.systemManageBtn.user_permissionSettings"
                   v-if="row.SysUserName !== 'admin'"
                   :type="row.IsEnabled ? 'danger' : 'primary'"
                   link

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

@@ -43,6 +43,8 @@ function allowDrag() {
 }
 function allowDrop(draggingNode, dropNode, type) {
   // 仅支持同部门下,同级节点相互排序
+  console.log(draggingNode, dropNode, type);
+  
   let canDrop = false
   if (draggingNode.level === dropNode.level && draggingNode.data.ParentId === dropNode.data.ParentId && type !== 'inner') {
     canDrop = true
@@ -256,7 +258,7 @@ function getFormTitle(e) {
           :data="departOpts"
           :props="departTreeKeys"
           :current-node-key="departActive"
-          :draggable="hasPermission('department:move')"
+          :draggable="hasPermission(permissionBtn.systemManageBtn.user_moveDept)"
           check-on-click-node
           :expand-on-click-node="false"
           node-key="SysDepartmentId"
@@ -293,8 +295,8 @@ function getFormTitle(e) {
                 <svg-icon
                   name="edit2"
                   size="16px"
-                  v-if="hasPermission('department:edit')"
-                  @click.stop="handleDepartOpt(node, data, 'edit')"
+                  v-if="hasPermission(permissionBtn.systemManageBtn.user_addDept)"
+                  @click.stop="handleDepartOpt(node, data, 'edit')" 
                 ></svg-icon>
                 <el-dropdown>
                   <svg-icon

+ 13 - 26
src/views/system/userList/components/EditUser.vue

@@ -17,6 +17,7 @@ const isEditAdmin=computed(()=>{
 watch(
   ()=>show.value,
   (n)=>{
+    formRef.value ? formRef.value.clearValidate() : '';
     if(n&&props.data){
       // console.log(props.data);
       formState=reactive({
@@ -26,7 +27,6 @@ watch(
         areacode: props.data.AreaCode,
         mobile: props.data.Phone,
         depart: props.data.SysDepartmentId,
-        email: props.data.Email,
         role: props.data.SysRoleId,
         province: props.data.Province,
         city: props.data.City,
@@ -40,7 +40,6 @@ watch(
         areacode: '86',
         mobile: '',
         depart: '',
-        email: '',
         role: '',
         province: '',
         city: '',
@@ -87,6 +86,7 @@ getRoleList()
 const formRules = {
   account: [{ required: true, message: '登录账号不能为空', trigger: 'blur' }],
   pwd: [{
+    required: true,
     validator: (rule, value, callback) => {
       if (value === '') {
         callback(new Error('请输入新密码'))
@@ -96,13 +96,15 @@ const formRules = {
       } else {
         callback()
       }
-    }
+    },
+    trigger: 'blur' 
   }],
   name: [{ required: true, message: '姓名不能为空', trigger: 'blur' }],
   mobile: [{
+    required: true,
     validator: (rule, value, callback) => {
-      if (value === '' && !formState.email) {
-        callback(new Error('手机号码和邮箱至少填一个'));
+      if (value === '') {
+        callback(new Error('请输入手机号码'));
       } else if (value && formState.areacode === '86' && !isMobileNo(value || '')) {
         callback(new Error('请输入正确的手机号格式'));
       } else if (formState.areacode !== '86' && isNaN(value?.trim())) {
@@ -110,18 +112,8 @@ const formRules = {
       } else {
         callback();
       }
-    }, trigger: 'blur'
-  }],
-  email: [{
-    validator: (rule, value, callback) => {
-      if (value === '' && !formState.mobile) {
-        callback(new Error('手机号码和邮箱至少填一个'));
-      } else if (value && !patternEmail.test(value)) {
-        callback(new Error('请输入正确的邮箱格式'));
-      } else {
-        callback();
-      }
-    }, trigger: 'blur'
+    },
+    trigger: 'blur'
   }],
   depart: [{ required: true, message: '部门分组不能为空', trigger: 'change' }],
   role: [{ required: true, message: '角色不能为空', trigger: 'change' }],
@@ -134,7 +126,6 @@ let formState = reactive({
   areacode: '86',
   mobile: '',
   depart: '',
-  email: '',
   role: '',
   province: '',
   city: '',
@@ -153,7 +144,6 @@ async function handleSave() {
     realname: formState.name,
     phone: formState.mobile,
     areacode: formState.areacode,
-    email: formState.email,
     Province: formState.province,
     City: formState.city,
     sysdepartmentid: formState.depart,
@@ -191,11 +181,11 @@ async function handleSave() {
       ref="formRef"
       label-width="80px"
     >
-      <el-form-item label="用户名" prop="account">
+      <el-form-item label="登录账号" prop="account">
         <el-input
           v-model="formState.account"
-          placeholder="建议使用邮箱前缀或者手机号码"
-          clearable
+          placeholder="请输入登录账号"
+          clearable 
           :disabled="isEditAdmin"
         />
       </el-form-item>
@@ -237,7 +227,7 @@ async function handleSave() {
           style="width: 60%; margin-left: 5%"
         />
       </el-form-item>
-      <el-form-item label="所属部门" prop="depart" v-if="!props.data&&!isEditAdmin">
+      <el-form-item label="所属部门" prop="depart" v-if="!isEditAdmin">
         <el-cascader
           style="width: 100%"
           :options="departArr"
@@ -254,9 +244,6 @@ async function handleSave() {
         >
         </el-cascader>
       </el-form-item>
-      <el-form-item label="邮箱" prop="email">
-        <el-input v-model="formState.email" placeholder="请输入邮箱" />
-      </el-form-item>
       <el-form-item label="分配角色" prop="role">
         <el-select v-model="formState.role" placeholder="分配角色" :disabled="isEditAdmin">
           <el-option

+ 2 - 2
vite.config.js

@@ -95,8 +95,8 @@ export default defineConfig(({ mode }) => {
       port: 8706,
       proxy: {
         "/v1": {
-          // target: "http://8.136.199.33:8705/adminapi",
-          target: "http://192.168.77.34:8706/adminapi",
+          target: "http://8.136.199.33:8705/adminapi",
+          // target: "http://192.168.77.34:8706/adminapi",
           changeOrigin: true,
           rewrite: (path) => path.replace(/^\/v1/, ""),
         },