Browse Source

fix: 2025-3-11

cmy 9 months ago
parent
commit
1c502c13b5
44 changed files with 2146 additions and 556 deletions
  1. 12 0
      src/api/money/index.ts
  2. 17 0
      src/api/money/types.ts
  3. 6 0
      src/api/order/index.ts
  4. 11 0
      src/api/order/types.ts
  5. 15 0
      src/api/regulate/index.ts
  6. 6 0
      src/api/regulate/types.ts
  7. 3 0
      src/api/staff/index.ts
  8. 8 6
      src/api/staff/types.ts
  9. 4 0
      src/components/SalespersonList/index.ts
  10. 46 0
      src/components/SalespersonList/src/SalespersonButtom.vue
  11. 136 0
      src/components/SalespersonList/src/SalespersonList.vue
  12. 1 1
      src/components/UserList/src/userButtom.vue
  13. 4 0
      src/components/WorkerList/index.ts
  14. 46 0
      src/components/WorkerList/src/workerButtom.vue
  15. 136 0
      src/components/WorkerList/src/workerList.vue
  16. 2 2
      src/components/designerList/index.ts
  17. 1 1
      src/components/designerList/src/desgnerButtom.vue
  18. 0 0
      src/components/designerList/src/desgnerList.vue
  19. 39 0
      src/router/model/Money.ts
  20. 23 0
      src/router/model/Order.ts
  21. 32 0
      src/router/model/Regulate.ts
  22. 2 2
      src/router/model/Staff.ts
  23. 1 1
      src/views/Authorization/Menu/Menu.vue
  24. 1 5
      src/views/Goods/list/index.vue
  25. 228 0
      src/views/Money/balance/index.vue
  26. 225 0
      src/views/Money/commission/index.vue
  27. 203 0
      src/views/Money/withdrawal/index.vue
  28. 231 0
      src/views/Order/orderList/index.vue
  29. 103 0
      src/views/Regulate/team/components/Write.vue
  30. 203 0
      src/views/Regulate/team/index.vue
  31. 13 0
      src/views/Staff/designer/components/Write.vue
  32. 62 29
      src/views/Staff/designer/index.vue
  33. 1 2
      src/views/Staff/group/components/designerWrite.vue
  34. 64 286
      src/views/Staff/group/components/workerWrite.vue
  35. 8 9
      src/views/Staff/group/designer.vue
  36. 20 111
      src/views/Staff/group/worker.vue
  37. 84 2
      src/views/Staff/salesman/components/Write.vue
  38. 10 10
      src/views/Staff/salesman/index.vue
  39. 13 0
      src/views/Staff/worker/components/Write.vue
  40. 68 37
      src/views/Staff/worker/index.vue
  41. 2 0
      src/views/System/log/index.vue
  42. 1 6
      src/views/User/level/index.vue
  43. 52 41
      src/views/User/list/components/DetailFrom.vue
  44. 3 5
      src/views/User/list/index.vue

+ 12 - 0
src/api/money/index.ts

@@ -0,0 +1,12 @@
+import request from '@/axios'
+import { REQUEST_BASE } from '@/constants'
+import { searchMoney, searchWithdrawal } from './types'
+export const getMoneyLog = (params: searchMoney): Promise<IResponse> => {
+  return request.get({ url: `${REQUEST_BASE}/finance/money/log`, params })
+}
+export const getBrokerageLog = (params: searchMoney): Promise<IResponse> => {
+  return request.get({ url: `${REQUEST_BASE}/finance/brokerage/log`, params })
+}
+export const getExtract = (params: searchWithdrawal): Promise<IResponse> => {
+  return request.get({ url: `${REQUEST_BASE}/finance/extract`, params })
+}

+ 17 - 0
src/api/money/types.ts

@@ -0,0 +1,17 @@
+export interface searchMoney {
+  page?: number
+  limit?: number
+  uid?: number | string
+  nickname?: string
+  start_time?: string //开始时间
+  end_time?: string //结束时间
+  type?: string //申请数据类型
+}
+export interface searchWithdrawal {
+  page?: number
+  limit?: number
+  uid?: number | string
+  real_name?: string
+  time?: string //时间范围查询
+  status?: string //审核状态
+}

+ 6 - 0
src/api/order/index.ts

@@ -0,0 +1,6 @@
+import request from '@/axios'
+import { REQUEST_BASE } from '@/constants'
+import { searchOrder } from './types'
+export const getOrderList = (params: searchOrder): Promise<IResponse> => {
+  return request.get({ url: `${REQUEST_BASE}/decoration/order`, params })
+}

+ 11 - 0
src/api/order/types.ts

@@ -0,0 +1,11 @@
+export interface searchOrder {
+  page?: number
+  limit?: number
+  uid?: number | string
+  order_id?: string //订单id
+  store_id?: string //门店id
+  salesperson_id?: string //业务员
+  real_name?: string //用户昵称
+  step?: string | number //步骤:0=待上门,1=待付款,2=待发货,3=待收货,4=待评价,5=已完成
+  time?: string //添加时间范围
+}

+ 15 - 0
src/api/regulate/index.ts

@@ -0,0 +1,15 @@
+import request from '@/axios'
+import { REQUEST_BASE } from '@/constants'
+import { salespersonLevelData } from './types'
+export const getSalespersonLevel = (): Promise<IResponse> => {
+  return request.get({ url: `${REQUEST_BASE}/decoration/salesperson_level` })
+}
+export const addSalespersonLevel = (data: salespersonLevelData): Promise<IResponse> => {
+  return request.post({ url: `${REQUEST_BASE}/decoration/salesperson_level`, data })
+}
+export const delSalespersonLevel = (id: number): Promise<IResponse> => {
+  return request.delete({ url: `${REQUEST_BASE}/decoration/salesperson_level/${id}` })
+}
+export const putSalespersonLevel = (data: salespersonLevelData): Promise<IResponse> => {
+  return request.put({ url: `${REQUEST_BASE}/decoration/salesperson_level/${data.id}`, data })
+}

+ 6 - 0
src/api/regulate/types.ts

@@ -0,0 +1,6 @@
+export interface salespersonLevelData {
+  id?: number
+  name: string
+  award_ratio?: string //分成百分比
+  info?: string //分成说明
+}

+ 3 - 0
src/api/staff/index.ts

@@ -69,6 +69,9 @@ export const getStaffSalesperson = (params: {
 }): Promise<IResponse> => {
   return request.get({ url: `${REQUEST_BASE}/decoration/salesperson`, params })
 }
+export const getStaffSalespersonDetail = (id: number): Promise<IResponse> => {
+  return request.get({ url: `${REQUEST_BASE}/decoration/salesperson/${id}` })
+}
 export const addStaffSalesperson = (data: SalespersonData): Promise<IResponse> => {
   return request.post({ url: `${REQUEST_BASE}/decoration/salesperson`, data })
 }

+ 8 - 6
src/api/staff/types.ts

@@ -4,6 +4,7 @@ export interface designerSearch {
   gender?: string
   tag_list?: string
   name?: string
+  group_id?: string | number
 }
 interface staff {
   id?: number
@@ -18,12 +19,17 @@ interface staff {
   latitude: string | number //纬度
   detail_address: string
 }
-
 export interface designerData extends staff {
   gender: 0 | 1 | 2 //性别 0保密 1男 2女
   birth_day_time?: string | number //生日
-  tag_list: Array<string> //标签
+  tag_list?: Array<string> //标签
   start_job_year: string
+  group_id?: number | ''
+}
+export interface SalespersonData extends staff {
+  level?: number
+  parent_id?: number
+  salesperson_commission?: number //分成比例%
 }
 export interface staffCategoryData {
   id?: number
@@ -38,10 +44,6 @@ export interface jobsData {
   cate_id: number
   default_price: number
 }
-export interface SalespersonData extends staff {
-  level?: number
-  parent_id?: number
-}
 
 export interface staffGroupSearch {
   page?: number

+ 4 - 0
src/components/SalespersonList/index.ts

@@ -0,0 +1,4 @@
+import SalespersonList from './src/SalespersonList.vue'
+import SalespersonButtom from './src/SalespersonButtom.vue'
+
+export { SalespersonList, SalespersonButtom }

+ 46 - 0
src/components/SalespersonList/src/SalespersonButtom.vue

@@ -0,0 +1,46 @@
+<script setup lang="tsx">
+import { ref, watch } from 'vue'
+import { getStaffSalespersonDetail } from '@/api/staff'
+import { ElAvatar, ElText } from 'element-plus'
+import list from './SalespersonList.vue'
+const modelValue = defineModel<number>({
+  default: 0
+})
+const detail = ref<any>()
+const showDrawer = ref(false)
+const emit = defineEmits(['change'])
+const checkedUser = async (res: any) => {
+  detail.value = res
+  emit('change', res)
+  showDrawer.value = false
+}
+watch(
+  () => modelValue.value,
+  async (value) => {
+    if (!value) return
+    const res = await getStaffSalespersonDetail(value)
+    if (res) {
+      detail.value = res.data
+    }
+  },
+  {
+    deep: true,
+    immediate: true
+  }
+)
+</script>
+<template>
+  <BaseButton v-if="detail" size="large" @click="showDrawer = true">
+    <div class="flex items-center">
+      <ElAvatar shape="circle" size="small" :src="detail?.avatar">
+        <img src="https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png" />
+      </ElAvatar>
+      <div class="flex flex-col ml-5px items-start justify-start">
+        <ElText class="self-start!">{{ detail?.name }}</ElText>
+        <ElText size="small" class="self-start!"> ID:{{ detail?.id }} </ElText>
+      </div>
+    </div>
+  </BaseButton>
+  <BaseButton v-else @click="showDrawer = true">选择业务员</BaseButton>
+  <list v-model="showDrawer" @confirm="checkedUser" />
+</template>

+ 136 - 0
src/components/SalespersonList/src/SalespersonList.vue

@@ -0,0 +1,136 @@
+<script setup lang="tsx">
+import { ElDrawer, ElAvatar } from 'element-plus'
+import { reactive, ref, unref } from 'vue'
+import { useTable } from '@/hooks/web/useTable'
+import { Table, TableColumn } from '@/components/Table'
+import { BaseButton } from '@/components/Button'
+import { Search } from '@/components/Search'
+import { designerSearch } from '@/api/staff/types'
+import { FormSchema } from '@/components/Form'
+import { getStaffSalesperson } from '@/api/staff'
+
+const { tableRegister, tableState, tableMethods } = useTable({
+  fetchDataApi: async () => {
+    const { data } = await getStaffSalesperson({
+      page: unref(currentPage) || 1,
+      limit: unref(pageSize) || 10,
+      ...unref(searchParams)
+    })
+    return {
+      list: data.list || [],
+      total: data.count || 0
+    }
+  }
+})
+const { dataList, loading, currentPage, pageSize, total } = tableState
+const { getList } = tableMethods
+const modelValue = defineModel<boolean>()
+
+const tableColumns = reactive<TableColumn[]>([
+  {
+    field: 'id',
+    label: 'ID',
+    align: 'center',
+    headerAlign: 'center',
+    minWidth: '60px'
+  },
+  {
+    field: 'info',
+    label: '头像',
+    align: 'center',
+    headerAlign: 'center',
+    minWidth: '60px',
+    slots: {
+      default: (data: any) => {
+        const row = data.row
+        return (
+          <>
+            <ElAvatar shape="circle" size="small" src={row.avatar}>
+              <img src="https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png" />
+            </ElAvatar>
+          </>
+        )
+      }
+    }
+  },
+  {
+    field: 'name',
+    label: '姓名',
+    minWidth: '90px'
+  },
+  {
+    field: 'phone',
+    label: '手机号',
+    minWidth: '130px'
+  },
+  {
+    field: 'detail_address',
+    label: '地址',
+    minWidth: '130px'
+  },
+  {
+    field: 'action',
+    label: '操作',
+    width: '90px',
+    slots: {
+      default: (data: any) => {
+        const row = data.row
+        return (
+          <>
+            <BaseButton type="primary" onClick={() => confirm(row)}>
+              选择
+            </BaseButton>
+          </>
+        )
+      }
+    }
+  }
+])
+
+const emit = defineEmits(['confirm'])
+const confirm = (row: any) => {
+  const data = Object.assign({}, row)
+  emit('confirm', data)
+}
+const searchSchema = reactive<FormSchema[]>([
+  {
+    field: 'name',
+    label: '搜索',
+    component: 'Input',
+    componentProps: {
+      placeholder: '姓名查询'
+    }
+  }
+])
+const searchParams = ref<designerSearch>({
+  name: ''
+})
+const setSearchParams = (data: any) => {
+  searchParams.value = data
+  getList()
+}
+</script>
+
+<template>
+  <ElDrawer v-model="modelValue" title="选择用户" size="700px">
+    <Search :schema="searchSchema" @reset="setSearchParams" @search="setSearchParams" />
+    <div class="mt-10px"></div>
+    <Table
+      v-model:current-page="currentPage"
+      v-model:page-size="pageSize"
+      :pagination="{
+        total
+      }"
+      :columns="tableColumns"
+      stripe
+      :data="dataList"
+      :loading="loading"
+      @register="tableRegister"
+    />
+    <template #footer>
+      <div>
+        <BaseButton @click="() => (modelValue = false)">关闭</BaseButton>
+      </div>
+    </template>
+  </ElDrawer>
+</template>

+ 1 - 1
src/components/UserList/src/userButtom.vue

@@ -32,7 +32,7 @@ watch(
 <template>
   <BaseButton v-if="userDetail" size="large" @click="showDrawer = true">
     <div class="flex items-center">
-      <ElAvatar shape="circle" size="small" src="{userDetail?.value?.avatar}">
+      <ElAvatar shape="circle" size="small" :src="userDetail?.avatar">
         <img src="https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png" />
       </ElAvatar>
       <div class="flex flex-col ml-5px items-start justify-start">

+ 4 - 0
src/components/WorkerList/index.ts

@@ -0,0 +1,4 @@
+import WorkerList from './src/workerList.vue'
+import WorkerButtom from './src/workerButtom.vue'
+
+export { WorkerList, WorkerButtom }

+ 46 - 0
src/components/WorkerList/src/workerButtom.vue

@@ -0,0 +1,46 @@
+<script setup lang="tsx">
+import { ref, watch } from 'vue'
+import { getWorkerDetail } from '@/api/staff'
+import { ElAvatar, ElText } from 'element-plus'
+import list from './workerList.vue'
+const modelValue = defineModel<number>({
+  default: 0
+})
+const detail = ref<any>()
+const showDrawer = ref(false)
+const emit = defineEmits(['change'])
+const checkedUser = async (res: any) => {
+  detail.value = res
+  emit('change', res)
+  showDrawer.value = false
+}
+watch(
+  () => modelValue.value,
+  async (value) => {
+    if (!value) return
+    const res = await getWorkerDetail(value)
+    if (res) {
+      detail.value = res.data
+    }
+  },
+  {
+    deep: true,
+    immediate: true
+  }
+)
+</script>
+<template>
+  <BaseButton v-if="detail" size="large" @click="showDrawer = true">
+    <div class="flex items-center">
+      <ElAvatar shape="circle" size="small" :src="detail?.avatar">
+        <img src="https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png" />
+      </ElAvatar>
+      <div class="flex flex-col ml-5px items-start justify-start">
+        <ElText class="self-start!">{{ detail?.name }}</ElText>
+        <ElText size="small" class="self-start!"> ID:{{ detail?.id }} </ElText>
+      </div>
+    </div>
+  </BaseButton>
+  <BaseButton v-else @click="showDrawer = true">选择装修工</BaseButton>
+  <list v-model="showDrawer" @confirm="checkedUser" />
+</template>

+ 136 - 0
src/components/WorkerList/src/workerList.vue

@@ -0,0 +1,136 @@
+<script setup lang="tsx">
+import { ElDrawer, ElAvatar } from 'element-plus'
+import { reactive, ref, unref } from 'vue'
+import { useTable } from '@/hooks/web/useTable'
+import { Table, TableColumn } from '@/components/Table'
+import { BaseButton } from '@/components/Button'
+import { Search } from '@/components/Search'
+import { designerSearch } from '@/api/staff/types'
+import { FormSchema } from '@/components/Form'
+import { getWorker } from '@/api/staff'
+
+const { tableRegister, tableState, tableMethods } = useTable({
+  fetchDataApi: async () => {
+    const { data } = await getWorker({
+      page: unref(currentPage) || 1,
+      limit: unref(pageSize) || 10,
+      ...unref(searchParams)
+    })
+    return {
+      list: data.list || [],
+      total: data.count || 0
+    }
+  }
+})
+const { dataList, loading, currentPage, pageSize, total } = tableState
+const { getList } = tableMethods
+const modelValue = defineModel<boolean>()
+
+const tableColumns = reactive<TableColumn[]>([
+  {
+    field: 'id',
+    label: 'ID',
+    align: 'center',
+    headerAlign: 'center',
+    minWidth: '60px'
+  },
+  {
+    field: 'info',
+    label: '头像',
+    align: 'center',
+    headerAlign: 'center',
+    minWidth: '60px',
+    slots: {
+      default: (data: any) => {
+        const row = data.row
+        return (
+          <>
+            <ElAvatar shape="circle" size="small" src={row.avatar}>
+              <img src="https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png" />
+            </ElAvatar>
+          </>
+        )
+      }
+    }
+  },
+  {
+    field: 'name',
+    label: '姓名',
+    minWidth: '90px'
+  },
+  {
+    field: 'phone',
+    label: '手机号',
+    minWidth: '130px'
+  },
+  {
+    field: 'detail_address',
+    label: '地址',
+    minWidth: '130px'
+  },
+  {
+    field: 'action',
+    label: '操作',
+    width: '90px',
+    slots: {
+      default: (data: any) => {
+        const row = data.row
+        return (
+          <>
+            <BaseButton type="primary" onClick={() => confirm(row)}>
+              选择
+            </BaseButton>
+          </>
+        )
+      }
+    }
+  }
+])
+
+const emit = defineEmits(['confirm'])
+const confirm = (row: any) => {
+  const data = Object.assign({}, row)
+  emit('confirm', data)
+}
+const searchSchema = reactive<FormSchema[]>([
+  {
+    field: 'name',
+    label: '搜索',
+    component: 'Input',
+    componentProps: {
+      placeholder: '姓名查询'
+    }
+  }
+])
+const searchParams = ref<designerSearch>({
+  name: ''
+})
+const setSearchParams = (data: any) => {
+  searchParams.value = data
+  getList()
+}
+</script>
+
+<template>
+  <ElDrawer v-model="modelValue" title="选择用户" size="700px">
+    <Search :schema="searchSchema" @reset="setSearchParams" @search="setSearchParams" />
+    <div class="mt-10px"></div>
+    <Table
+      v-model:current-page="currentPage"
+      v-model:page-size="pageSize"
+      :pagination="{
+        total
+      }"
+      :columns="tableColumns"
+      stripe
+      :data="dataList"
+      :loading="loading"
+      @register="tableRegister"
+    />
+    <template #footer>
+      <div>
+        <BaseButton @click="() => (modelValue = false)">关闭</BaseButton>
+      </div>
+    </template>
+  </ElDrawer>
+</template>

+ 2 - 2
src/components/designerList/index.ts

@@ -1,4 +1,4 @@
-import DesignerList from './src/list.vue'
-import DesignerButtom from './src/buttom.vue'
+import DesignerList from './src/desgnerList.vue'
+import DesignerButtom from './src/desgnerButtom.vue'
 
 export { DesignerList, DesignerButtom }

+ 1 - 1
src/components/designerList/src/buttom.vue → src/components/designerList/src/desgnerButtom.vue

@@ -2,7 +2,7 @@
 import { ref, watch } from 'vue'
 import { getDesignerDetail } from '@/api/staff'
 import { ElAvatar, ElText } from 'element-plus'
-import list from './list.vue'
+import list from './desgnerList.vue'
 const modelValue = defineModel<number>({
   default: 0
 })

+ 0 - 0
src/components/designerList/src/list.vue → src/components/designerList/src/desgnerList.vue


+ 39 - 0
src/router/model/Money.ts

@@ -0,0 +1,39 @@
+import { Layout } from '@/utils/routerHelper'
+const pre = 'money'
+
+export default {
+  path: `/${pre}`,
+  component: Layout,
+  name: `${pre}`,
+  meta: {
+    title: '财务管理',
+    icon: 'vi-eos-icons:role-binding',
+    alwaysShow: true
+  },
+  children: [
+    {
+      path: 'balance',
+      component: () => import('@/views/Money/balance/index.vue'),
+      name: `${pre}-balance`,
+      meta: {
+        title: '余额流水'
+      }
+    },
+    {
+      path: 'commission',
+      component: () => import('@/views/Money/commission/index.vue'),
+      name: `${pre}-commission`,
+      meta: {
+        title: '佣金流水'
+      }
+    },
+    {
+      path: 'withdrawal',
+      component: () => import('@/views/Money/withdrawal/index.vue'),
+      name: `${pre}-commission`,
+      meta: {
+        title: '提现申请'
+      }
+    }
+  ]
+}

+ 23 - 0
src/router/model/Order.ts

@@ -0,0 +1,23 @@
+import { Layout } from '@/utils/routerHelper'
+const pre = 'order'
+
+export default {
+  path: `/${pre}`,
+  component: Layout,
+  name: `${pre}`,
+  meta: {
+    title: '订单管理',
+    icon: 'vi-eos-icons:role-binding',
+    alwaysShow: true
+  },
+  children: [
+    {
+      path: 'list',
+      component: () => import('@/views/Order/orderList/index.vue'),
+      name: `${pre}-list`,
+      meta: {
+        title: '订单列表'
+      }
+    }
+  ]
+}

+ 32 - 0
src/router/model/Regulate.ts

@@ -0,0 +1,32 @@
+import { Layout } from '@/utils/routerHelper'
+const pre = 'regulate'
+
+export default {
+  path: `/${pre}`,
+  component: Layout,
+  name: `${pre}`,
+  meta: {
+    title: '业务管理',
+    icon: 'vi-eos-icons:role-binding',
+    alwaysShow: true
+  },
+  children: [
+    {
+      path: 'team',
+      component: () => import('@/views/Regulate/team/index.vue'),
+      name: `${pre}-team`,
+      meta: {
+        title: '团队等级'
+      }
+    },
+    {
+      path: 'award',
+      component: () => import('@/views/System/set/set.vue'),
+      name: `${pre}-award`,
+      props: { id: 23 },
+      meta: {
+        title: '业务设置'
+      }
+    }
+  ]
+}

+ 2 - 2
src/router/model/Staff.ts

@@ -54,7 +54,7 @@ export default {
     {
       path: 'group/designer',
       component: () => import('@/views/Staff/group/designer.vue'),
-      name: `${pre}-group`,
+      name: `${pre}-group-designer`,
       meta: {
         title: '设计师分组'
       }
@@ -62,7 +62,7 @@ export default {
     {
       path: 'group/worker',
       component: () => import('@/views/Staff/group/worker.vue'),
-      name: `${pre}-worker`,
+      name: `${pre}-group-worker`,
       meta: {
         title: '装修工分组'
       }

+ 1 - 1
src/views/Authorization/Menu/Menu.vue

@@ -184,7 +184,7 @@ const searchSchema = reactive<FormSchema[]>([
   {
     field: 'type',
     label: '类型',
-    value: 1,
+    value: 1, //1总后台2门店
     component: 'Input',
     hidden: true
   },

+ 1 - 5
src/views/Goods/list/index.vue

@@ -432,7 +432,7 @@ const audit = async (id, is_verify, refusal) => {
         >{{ index }}
       </ElTabPane>
     </ElTabs>
-    <div class="searchBox">
+    <div class="max-w-1000px">
       <Search
         :schema="searchSchema"
         @reset="setSearchParams"
@@ -544,10 +544,6 @@ const audit = async (id, is_verify, refusal) => {
   />
 </template>
 <style lang="less">
-.searchBox {
-  max-width: 1000px;
-}
-
 .example-showcase .el-dropdown-link {
   display: flex;
   color: var(--el-color-primary);

+ 228 - 0
src/views/Money/balance/index.vue

@@ -0,0 +1,228 @@
+<script setup lang="tsx">
+import { reactive, ref, watch, unref } from 'vue'
+import { getMoneyLog } from '@/api/money'
+import { useTable } from '@/hooks/web/useTable'
+import { Table, TableColumn } from '@/components/Table'
+import { ContentWrap } from '@/components/ContentWrap'
+import { FormSchema } from '@/components/Form'
+import { Search } from '@/components/Search'
+import { useSearch } from '@/hooks/web/useSearch'
+import { searchTime } from '@/utils/searchTime'
+import { searchMoney } from '@/api/money/types'
+const { searchRegister, searchMethods } = useSearch()
+const { setSchema } = searchMethods
+
+const { tableRegister, tableState, tableMethods } = useTable({
+  fetchDataApi: async () => {
+    const res = await getMoneyLog({
+      ...searchParams.value,
+      page: unref(currentPage) || 1,
+      limit: unref(pageSize) || 10
+    })
+    if (types.value.length == 0) {
+      types.value = res.data.types.list
+    }
+    return {
+      list:
+        res.data.data.data.map((ee) => {
+          return ee
+        }) || [],
+      total: res.data.data.count || 0
+    }
+  }
+})
+const { dataList, loading, total, currentPage, pageSize } = tableState
+const { getList } = tableMethods
+
+const tableColumns = reactive<TableColumn[]>([
+  {
+    field: 'id',
+    label: 'ID',
+    align: 'center',
+    width: 70
+  },
+  {
+    field: 'nickname',
+    label: '用户',
+    minWidth: 100
+  },
+  {
+    field: 'uid',
+    label: '用户UID',
+    align: 'center',
+    headerAlign: 'center',
+    minWidth: 80
+  },
+  {
+    field: 'title',
+    label: '类型',
+    minWidth: 100
+  },
+  {
+    field: 'number',
+    label: '变动数量',
+    minWidth: 100,
+    slots: {
+      default: (data: any) => {
+        const row = data.row
+        if (row.pm > 0) {
+          return (
+            <>
+              <div class={'color-green'}>+{row.number}</div>
+            </>
+          )
+        } else {
+          return (
+            <>
+              <div class={'color-red'}>-{row.number}</div>
+            </>
+          )
+        }
+      }
+    }
+  },
+  {
+    field: 'balance',
+    label: '变动后金额',
+    minWidth: 100
+  },
+  {
+    field: 'add_time',
+    label: '时间',
+    minWidth: 160
+  },
+  {
+    field: 'mark',
+    label: '备注',
+    minWidth: 140
+  },
+  {
+    field: 'status',
+    label: '状态',
+    width: 60,
+    headerAlign: 'center',
+    align: 'center',
+    slots: {
+      default: (data: any) => {
+        const row = data.row
+        if (row.status == 1) {
+          return (
+            <>
+              <div class={'color-green'}>有效</div>
+            </>
+          )
+        } else {
+          return (
+            <>
+              <div class={'color-red'}>无效</div>
+            </>
+          )
+        }
+      }
+    }
+  }
+])
+
+const searchSchema = reactive<FormSchema[]>([
+  {
+    field: 'uid',
+    label: 'uid',
+    component: 'Input',
+    componentProps: {
+      type: 'number',
+      placeholder: '请输入用户UID'
+    }
+  },
+  {
+    field: 'nickname',
+    label: '用户昵称',
+    component: 'Input',
+    componentProps: {
+      type: 'number',
+      placeholder: '请输入用户昵称'
+    }
+  },
+  {
+    field: 'type',
+    label: '类型',
+    component: 'Select',
+    componentProps: {
+      placeholder: '请选择类型',
+      options: []
+    }
+  },
+  searchTime
+])
+const searchParams = ref<searchMoney>({
+  uid: '',
+  nickname: '',
+  start_time: '',
+  end_time: '',
+  type: ''
+})
+const setSearchParams = (data: any) => {
+  const search = searchParams.value
+  search.uid = data.uid || ''
+  console.log(data, 'data')
+  search.nickname = data.nickname || ''
+  if (data.time) {
+    search.start_time = data.time[0] || ''
+    search.end_time = data.time[1] || ''
+  } else {
+    search.start_time = ''
+    search.end_time = ''
+  }
+  search.type = data.type || ''
+  // searchParams.value = data
+  getList()
+}
+const types = ref<any[]>([]) //查询类型
+watch(
+  () => types.value,
+  (val) => {
+    console.log(val, 'val')
+    if (val) {
+      setSchema([
+        {
+          field: 'type',
+          path: 'componentProps.options',
+          value: val.map((res) => {
+            return {
+              label: res.title,
+              value: res.type
+            }
+          })
+        }
+      ])
+      searchParams.value.type = ''
+    }
+  }
+)
+</script>
+
+<template>
+  <ContentWrap>
+    <Search
+      isCol
+      :schema="searchSchema"
+      @reset="setSearchParams"
+      @search="setSearchParams"
+      @register="searchRegister"
+    />
+    <div class="mb-10px"> </div>
+    <Table
+      v-model:current-page="currentPage"
+      v-model:page-size="pageSize"
+      :columns="tableColumns"
+      default-expand-all
+      node-key="id"
+      stripe
+      :data="dataList"
+      :loading="loading"
+      @register="tableRegister"
+      :pagination="{
+        total
+      }"
+    />
+  </ContentWrap>
+</template>

+ 225 - 0
src/views/Money/commission/index.vue

@@ -0,0 +1,225 @@
+<script setup lang="tsx">
+import { reactive, ref, watch, unref } from 'vue'
+import { getBrokerageLog } from '@/api/money'
+import { useTable } from '@/hooks/web/useTable'
+import { Table, TableColumn } from '@/components/Table'
+import { ContentWrap } from '@/components/ContentWrap'
+import { FormSchema } from '@/components/Form'
+import { Search } from '@/components/Search'
+import { useSearch } from '@/hooks/web/useSearch'
+import { searchTime } from '@/utils/searchTime'
+import { searchMoney } from '@/api/money/types'
+const { searchRegister, searchMethods } = useSearch()
+const { setSchema } = searchMethods
+const { tableRegister, tableState, tableMethods } = useTable({
+  fetchDataApi: async () => {
+    const res = await getBrokerageLog({
+      ...searchParams.value,
+      page: unref(currentPage) || 1,
+      limit: unref(pageSize) || 10
+    })
+    if (types.value.length == 0) {
+      types.value = res.data.types.list
+    }
+    return {
+      list:
+        res.data.data.data.map((ee) => {
+          return ee
+        }) || [],
+      total: res.data.data.count || 0
+    }
+  }
+})
+const { dataList, loading, total, currentPage, pageSize } = tableState
+const { getList } = tableMethods
+
+const tableColumns = reactive<TableColumn[]>([
+  {
+    field: 'id',
+    label: 'ID',
+    align: 'center',
+    width: 70
+  },
+  {
+    field: 'nickname',
+    label: '用户',
+    minWidth: 100
+  },
+  {
+    field: 'uid',
+    label: '用户UID',
+    align: 'center',
+    headerAlign: 'center',
+    minWidth: 80
+  },
+  {
+    field: 'title',
+    label: '类型',
+    minWidth: 100
+  },
+  {
+    field: 'number',
+    label: '变动数量',
+    minWidth: 100,
+    slots: {
+      default: (data: any) => {
+        const row = data.row
+        if (row.pm > 0) {
+          return (
+            <>
+              <div class={'color-green'}>+{row.number}</div>
+            </>
+          )
+        } else {
+          return (
+            <>
+              <div class={'color-red'}>-{row.number}</div>
+            </>
+          )
+        }
+      }
+    }
+  },
+  {
+    field: 'balance',
+    label: '变动后金额',
+    minWidth: 100
+  },
+  {
+    field: 'add_time',
+    label: '时间',
+    minWidth: 160
+  },
+  {
+    field: 'mark',
+    label: '备注',
+    minWidth: 140
+  },
+  {
+    field: 'status',
+    label: '状态',
+    width: 60,
+    headerAlign: 'center',
+    align: 'center',
+    slots: {
+      default: (data: any) => {
+        const row = data.row
+        if (row.status == 1) {
+          return (
+            <>
+              <div class={'color-green'}>有效</div>
+            </>
+          )
+        } else {
+          return (
+            <>
+              <div class={'color-red'}>无效</div>
+            </>
+          )
+        }
+      }
+    }
+  }
+])
+
+const searchSchema = reactive<FormSchema[]>([
+  {
+    field: 'uid',
+    label: 'uid',
+    component: 'Input',
+    componentProps: {
+      type: 'number',
+      placeholder: '请输入用户UID'
+    }
+  },
+  {
+    field: 'nickname',
+    label: '用户昵称',
+    component: 'Input',
+    componentProps: {
+      type: 'number',
+      placeholder: '请输入用户昵称'
+    }
+  },
+  {
+    field: 'type',
+    label: '类型',
+    component: 'Select',
+    componentProps: {
+      placeholder: '请选择类型',
+      options: []
+    }
+  },
+  searchTime
+])
+const searchParams = ref<searchMoney>({
+  uid: '',
+  nickname: '',
+  start_time: '',
+  end_time: '',
+  type: ''
+})
+const setSearchParams = (data: any) => {
+  const search = searchParams.value
+  search.uid = data.uid || ''
+  search.nickname = data.nickname || ''
+  if (data.time) {
+    search.start_time = data.time[0] || ''
+    search.end_time = data.time[1] || ''
+  } else {
+    search.start_time = ''
+    search.end_time = ''
+  }
+  search.type = data.type || ''
+  getList()
+}
+const types = ref<any[]>([]) //查询类型
+watch(
+  () => types.value,
+  (val) => {
+    console.log(val, 'val')
+    if (val) {
+      setSchema([
+        {
+          field: 'type',
+          path: 'componentProps.options',
+          value: val.map((res) => {
+            return {
+              label: res.title,
+              value: res.type
+            }
+          })
+        }
+      ])
+      searchParams.value.type = ''
+    }
+  }
+)
+</script>
+
+<template>
+  <ContentWrap>
+    <Search
+      isCol
+      :schema="searchSchema"
+      @reset="setSearchParams"
+      @search="setSearchParams"
+      @register="searchRegister"
+    />
+    <div class="mb-10px"> </div>
+    <Table
+      v-model:current-page="currentPage"
+      v-model:page-size="pageSize"
+      :columns="tableColumns"
+      default-expand-all
+      node-key="id"
+      stripe
+      :data="dataList"
+      :loading="loading"
+      @register="tableRegister"
+      :pagination="{
+        total
+      }"
+    />
+  </ContentWrap>
+</template>

+ 203 - 0
src/views/Money/withdrawal/index.vue

@@ -0,0 +1,203 @@
+<script setup lang="tsx">
+import { reactive, ref, unref } from 'vue'
+import { getExtract } from '@/api/money'
+import { useTable } from '@/hooks/web/useTable'
+import { Table, TableColumn } from '@/components/Table'
+import { ContentWrap } from '@/components/ContentWrap'
+import { FormSchema } from '@/components/Form'
+import { Search } from '@/components/Search'
+import { searchTime } from '@/utils/searchTime'
+import { searchWithdrawal } from '@/api/money/types'
+const { tableRegister, tableState, tableMethods } = useTable({
+  fetchDataApi: async () => {
+    const res = await getExtract({
+      ...searchParams.value,
+      page: unref(currentPage) || 1,
+      limit: unref(pageSize) || 10
+    })
+    return {
+      list:
+        res.data.data.data.map((ee) => {
+          return ee
+        }) || [],
+      total: res.data.data.count || 0
+    }
+  }
+})
+const { dataList, loading, total, currentPage, pageSize } = tableState
+const { getList } = tableMethods
+
+const tableColumns = reactive<TableColumn[]>([
+  {
+    field: 'id',
+    label: 'ID',
+    align: 'center',
+    width: 70
+  },
+  {
+    field: 'nickname',
+    label: '用户',
+    minWidth: 100
+  },
+  {
+    field: 'uid',
+    label: '用户UID',
+    align: 'center',
+    headerAlign: 'center',
+    minWidth: 80
+  },
+  {
+    field: 'title',
+    label: '类型',
+    minWidth: 100
+  },
+  {
+    field: 'number',
+    label: '变动数量',
+    minWidth: 100,
+    slots: {
+      default: (data: any) => {
+        const row = data.row
+        if (row.pm > 0) {
+          return (
+            <>
+              <div class={'color-green'}>+{row.number}</div>
+            </>
+          )
+        } else {
+          return (
+            <>
+              <div class={'color-red'}>-{row.number}</div>
+            </>
+          )
+        }
+      }
+    }
+  },
+  {
+    field: 'balance',
+    label: '变动后金额',
+    minWidth: 100
+  },
+  {
+    field: 'add_time',
+    label: '时间',
+    minWidth: 160
+  },
+  {
+    field: 'mark',
+    label: '备注',
+    minWidth: 140
+  },
+  {
+    field: 'status',
+    label: '状态',
+    width: 60,
+    headerAlign: 'center',
+    align: 'center',
+    slots: {
+      default: (data: any) => {
+        const row = data.row
+        if (row.status == 1) {
+          return (
+            <>
+              <div class={'color-green'}>有效</div>
+            </>
+          )
+        } else {
+          return (
+            <>
+              <div class={'color-red'}>无效</div>
+            </>
+          )
+        }
+      }
+    }
+  }
+])
+
+const searchSchema = reactive<FormSchema[]>([
+  {
+    field: 'uid',
+    label: 'uid',
+    component: 'Input',
+    componentProps: {
+      type: 'number',
+      placeholder: '请输入用户UID'
+    }
+  },
+  {
+    field: 'real_name',
+    label: '用户昵称',
+    component: 'Input',
+    componentProps: {
+      type: 'number',
+      placeholder: '请输入用户昵称'
+    }
+  },
+  {
+    field: 'status',
+    label: '状态',
+    component: 'Select',
+    componentProps: {
+      placeholder: '请选择类型',
+      options: [
+        {
+          label: '全部',
+          value: ''
+        },
+        {
+          label: '已通过',
+          value: 1
+        },
+        {
+          label: '待审核',
+          value: 0
+        },
+        {
+          label: '已拒绝',
+          value: 0
+        }
+      ]
+    }
+  },
+  searchTime
+])
+const searchParams = ref<searchWithdrawal>({
+  uid: '',
+  real_name: '',
+  time: '',
+  status: ''
+})
+const setSearchParams = (data: any) => {
+  searchParams.value = data
+  const search = searchParams.value
+  if (data.time) {
+    search.time = data.time.join('-')
+  } else {
+    search.time = ''
+  }
+  getList()
+}
+</script>
+
+<template>
+  <ContentWrap>
+    <Search isCol :schema="searchSchema" @reset="setSearchParams" @search="setSearchParams" />
+    <div class="mb-10px"> </div>
+    <Table
+      v-model:current-page="currentPage"
+      v-model:page-size="pageSize"
+      :columns="tableColumns"
+      default-expand-all
+      node-key="id"
+      stripe
+      :data="dataList"
+      :loading="loading"
+      @register="tableRegister"
+      :pagination="{
+        total
+      }"
+    />
+  </ContentWrap>
+</template>

+ 231 - 0
src/views/Order/orderList/index.vue

@@ -0,0 +1,231 @@
+<script setup lang="tsx">
+import { reactive, ref, unref } from 'vue'
+import { getOrderList } from '@/api/order'
+import { useTable } from '@/hooks/web/useTable'
+import { Table, TableColumn } from '@/components/Table'
+import { ContentWrap } from '@/components/ContentWrap'
+import { FormSchema } from '@/components/Form'
+import { Search } from '@/components/Search'
+import { searchTime } from '@/utils/searchTime'
+import { searchOrder } from '@/api/order/types'
+import { ElTabs, ElTabPane } from 'element-plus'
+const { tableRegister, tableState, tableMethods } = useTable({
+  fetchDataApi: async () => {
+    const res = await getOrderList({
+      ...searchParams.value,
+      page: unref(currentPage) || 1,
+      limit: unref(pageSize) || 10,
+      step: unref(activeName) || 0
+    })
+    return {
+      list: res.data.list || [],
+      total: res.data.count || 0
+    }
+  }
+})
+const { dataList, loading, total, currentPage, pageSize } = tableState
+const { getList } = tableMethods
+
+const tableColumns = reactive<TableColumn[]>([
+  {
+    field: 'id',
+    label: 'ID',
+    align: 'center',
+    width: 70
+  },
+  {
+    field: 'real_name',
+    label: '用户',
+    minWidth: 100,
+    slots: {
+      default: (data: any) => {
+        const row = data.row
+        return (
+          <>
+            <div>{row.real_name}</div>
+            <div>{row.phone}</div>
+          </>
+        )
+      }
+    }
+  },
+  {
+    field: 'uid',
+    label: '用户UID',
+    align: 'center',
+    headerAlign: 'center',
+    minWidth: 80
+  },
+  {
+    field: 'address',
+    label: '地址',
+    minWidth: 100
+  },
+  {
+    field: 'price',
+    label: '金额',
+    minWidth: 100
+  },
+  {
+    field: 'area',
+    label: '预估面积',
+    minWidth: 100
+  },
+  {
+    field: 'add_time',
+    label: '时间',
+    minWidth: 160
+  },
+  {
+    field: 'mark',
+    label: '备注',
+    minWidth: 140
+  },
+  {
+    field: 'status',
+    label: '状态',
+    width: 60,
+    headerAlign: 'center',
+    align: 'center',
+    slots: {
+      default: (data: any) => {
+        const row = data.row
+        if (row.status == 1) {
+          return (
+            <>
+              <div class={'color-green'}>有效</div>
+            </>
+          )
+        } else {
+          return (
+            <>
+              <div class={'color-red'}>无效</div>
+            </>
+          )
+        }
+      }
+    }
+  }
+])
+
+const searchSchema = reactive<FormSchema[]>([
+  {
+    field: 'uid',
+    label: 'UID',
+    component: 'Input',
+    componentProps: {
+      type: 'number',
+      placeholder: '请输入用户UID'
+    }
+  },
+  {
+    field: 'real_name',
+    label: '用户昵称',
+    component: 'Input',
+    componentProps: {
+      placeholder: '请输入用户昵称'
+    }
+  },
+  {
+    field: 'salesperson_id',
+    label: '业务员',
+    component: 'Input',
+    componentProps: {
+      type: 'number',
+      placeholder: '请选择业务员'
+    }
+  },
+  {
+    field: 'order_id',
+    label: '订单号',
+    component: 'Input',
+    componentProps: {
+      placeholder: '请输入订单号'
+    }
+  },
+  searchTime
+])
+const searchParams = ref<searchOrder>({
+  uid: '',
+  real_name: '',
+  time: '',
+  store_id: '',
+  salesperson_id: '',
+  step: '',
+  order_id: ''
+})
+const setSearchParams = (data: any) => {
+  searchParams.value = data
+  const search = searchParams.value
+  if (data.time) {
+    search.time = data.time.join('-')
+  } else {
+    search.time = ''
+  }
+  getList()
+}
+const activeName = ref(0)
+const tabsConfig = reactive([
+  {
+    title: '待上门',
+    total: 0,
+    step: 0
+  },
+  {
+    title: '待付款',
+    total: 0,
+    step: 1
+  },
+  {
+    title: '待发货',
+    total: 0,
+    step: 2
+  },
+  {
+    title: '待收货',
+    total: 0,
+    step: 3
+  },
+  {
+    title: '待评价',
+    total: 0,
+    step: 4
+  },
+  {
+    title: '已完成',
+    total: 0,
+    step: 5
+  }
+])
+</script>
+
+<template>
+  <ContentWrap>
+    <ElTabs v-model="activeName" @tab-click="getList">
+      <ElTabPane
+        v-for="(item, index) in tabsConfig"
+        :key="index"
+        :label="`${item.title}${item.total > 0 ? `(${item.total})` : ''}`"
+        :name="index"
+      />
+    </ElTabs>
+    <div class="max-w-1000px">
+      <Search isCol :schema="searchSchema" @reset="setSearchParams" @search="setSearchParams" />
+    </div>
+    <div class="mb-10px"> </div>
+    <Table
+      v-model:current-page="currentPage"
+      v-model:page-size="pageSize"
+      :columns="tableColumns"
+      default-expand-all
+      node-key="id"
+      stripe
+      :data="dataList"
+      :loading="loading"
+      @register="tableRegister"
+      :pagination="{
+        total
+      }"
+    />
+  </ContentWrap>
+</template>

+ 103 - 0
src/views/Regulate/team/components/Write.vue

@@ -0,0 +1,103 @@
+<script setup lang="tsx">
+import { Form, FormSchema } from '@/components/Form'
+import { useForm } from '@/hooks/web/useForm'
+import { PropType, watch, ref, onMounted } from 'vue'
+import { useValidator } from '@/hooks/web/useValidator'
+import { cloneDeep } from 'lodash-es'
+
+const { required } = useValidator()
+onMounted(() => {})
+const props = defineProps({
+  currentRow: {
+    type: Object as PropType<any>,
+    default: () => null
+  }
+})
+
+const formSchema = ref<FormSchema[]>([
+  {
+    field: 'name',
+    label: '等级名称',
+    component: 'Input',
+    colProps: {
+      span: 24
+    },
+    componentProps: {
+      placeholder: '请输入等级名称'
+    }
+  },
+  {
+    field: 'award_ratio',
+    label: '分成百分比',
+    component: 'Input',
+    colProps: {
+      span: 24
+    },
+    componentProps: {
+      type: 'number',
+      placeholder: '请输入等级'
+    }
+  },
+  {
+    field: 'info',
+    label: '分成说明',
+    component: 'Input',
+    colProps: {
+      span: 24
+    },
+    componentProps: {
+      type: 'textarea',
+      placeholder: '请输入分成说明',
+      row: 4
+    }
+  },
+  {
+    field: 'id',
+    label: '',
+    hidden: true,
+    component: 'Input'
+  }
+])
+
+const rules = ref({
+  name: [required('请填写团队等级')],
+  award_ratio: [required('请输入分成百分比')]
+})
+
+const { formRegister, formMethods } = useForm()
+const { setValues, getFormData, getElFormExpose } = formMethods
+
+const submit = async () => {
+  const elForm = await getElFormExpose()
+  const valid = await elForm?.validate().catch((err) => {
+    console.log(err)
+  })
+  if (valid) {
+    const formData = await getFormData()
+    return formData
+  }
+}
+
+watch(
+  () => props.currentRow,
+  async (value) => {
+    if (!value) return
+    const currentRow = cloneDeep(value)
+    setValues(currentRow)
+  },
+  {
+    deep: true,
+    immediate: true
+  }
+)
+
+defineExpose({
+  submit
+})
+</script>
+
+<template>
+  <div>
+    <Form :rules="rules" @register="formRegister" :schema="formSchema" />
+  </div>
+</template>

+ 203 - 0
src/views/Regulate/team/index.vue

@@ -0,0 +1,203 @@
+<script setup lang="tsx">
+import { reactive, ref, unref } from 'vue'
+import {
+  getSalespersonLevel,
+  addSalespersonLevel,
+  putSalespersonLevel,
+  delSalespersonLevel
+} from '@/api/regulate'
+import { useTable } from '@/hooks/web/useTable'
+import { useI18n } from '@/hooks/web/useI18n'
+import { Table, TableColumn } from '@/components/Table'
+import { ContentWrap } from '@/components/ContentWrap'
+import Write from './components/Write.vue'
+import { BaseButton } from '@/components/Button'
+import { ElMessage, ElDivider, ElMessageBox } from 'element-plus'
+import { Dialog } from '@/components/Dialog'
+import { salespersonLevelData } from '@/api/regulate/types'
+const { t } = useI18n()
+
+const { tableRegister, tableState, tableMethods } = useTable({
+  fetchDataApi: async () => {
+    const res = await getSalespersonLevel()
+    return {
+      list: res.data.list.map((re) => {
+        re.is_forever = re.is_forever == 1 ? true : false
+        re.is_show = re.is_show == 1 ? true : false
+        return re
+      }),
+      total: res.data.count || 0
+    }
+  }
+})
+const { dataList, loading, total, currentPage, pageSize } = tableState
+const { getList } = tableMethods
+
+const tableColumns = reactive<TableColumn[]>([
+  {
+    field: 'id',
+    label: 'ID',
+    align: 'center',
+    width: 70
+  },
+  {
+    field: 'name',
+    label: '等级名称',
+    minWidth: 100
+  },
+  {
+    field: 'award_ratio',
+    label: '分成(%)',
+    minWidth: 100
+  },
+  {
+    field: 'info',
+    label: '等级说明',
+    minWidth: 140
+  },
+  {
+    field: 'action',
+    label: t('userDemo.action'),
+    width: 110,
+    align: 'center',
+    headerAlign: 'center',
+    fixed: 'right',
+    slots: {
+      default: (data: any) => {
+        const row = data.row
+        return (
+          <>
+            <BaseButton link size="small" type="primary" onClick={() => action(row, 'edit')}>
+              {t('exampleDemo.edit')}
+            </BaseButton>
+            <ElDivider direction="vertical" />
+            <BaseButton link size="small" type="danger" onClick={() => delAction(row)}>
+              {t('exampleDemo.del')}
+            </BaseButton>
+          </>
+        )
+      }
+    }
+  }
+])
+
+const dialogVisible = ref(false)
+const currentRow = ref<salespersonLevelData>()
+const dialogTitle = ref('')
+const actionType = ref('')
+const writeRef = ref<ComponentRef<typeof Write>>()
+const saveLoading = ref(false)
+const action = async (row: any, type: string) => {
+  dialogTitle.value = t(type === 'edit' ? 'exampleDemo.edit' : 'exampleDemo.detail')
+  actionType.value = type
+  currentRow.value = {
+    id: row.id,
+    name: row.name,
+    award_ratio: row.award_ratio,
+    info: row.info
+  }
+  dialogVisible.value = true
+}
+
+const AddAction = () => {
+  dialogTitle.value = t('exampleDemo.add')
+  currentRow.value = undefined
+  dialogVisible.value = true
+  actionType.value = ''
+}
+
+const save = async () => {
+  const write = unref(writeRef)
+  const formData = await write?.submit()
+  if (formData) {
+    saveLoading.value = true
+    const data: salespersonLevelData = {
+      name: formData.name,
+      id: formData.id,
+      award_ratio: formData.award_ratio,
+      info: formData.info
+    }
+    try {
+      let res: any = {}
+      if (actionType.value === 'edit') {
+        data.id = formData.id
+        // console.log(data)
+        res = await putSalespersonLevel(data)
+      } else if (actionType.value === '') {
+        res = await addSalespersonLevel(data)
+      }
+      if (res?.status === 200) {
+        ElMessage({
+          showClose: true,
+          message: '保存成功',
+          type: 'success'
+        })
+        getList()
+        dialogVisible.value = false
+      }
+      saveLoading.value = false
+    } catch (error) {
+      console.log(error)
+    } finally {
+      saveLoading.value = false
+    }
+  }
+}
+
+const delAction = (row: any) => {
+  ElMessageBox.confirm('删除后无法恢复,是否删除用户等级?', {
+    confirmButtonText: '删除',
+    cancelButtonText: '取消',
+    type: 'warning'
+  })
+    .then(async () => {
+      const re = await delSalespersonLevel(row.id)
+      if (re) {
+        ElMessage({
+          showClose: true,
+          message: '删除成功',
+          type: 'success'
+        })
+      }
+      await getList()
+    })
+    .catch(() => {})
+}
+</script>
+
+<template>
+  <ContentWrap>
+    <div class="mb-10px">
+      <BaseButton type="primary" @click="AddAction">{{ t('exampleDemo.add') }}</BaseButton>
+    </div>
+    <Table
+      v-model:current-page="currentPage"
+      v-model:page-size="pageSize"
+      :columns="tableColumns"
+      default-expand-all
+      node-key="id"
+      stripe
+      :data="dataList"
+      :loading="loading"
+      @register="tableRegister"
+      :pagination="{
+        total
+      }"
+    />
+  </ContentWrap>
+
+  <Dialog v-model="dialogVisible" :title="dialogTitle" width="700px">
+    <Write v-if="actionType !== 'detail'" ref="writeRef" :current-row="currentRow" />
+    <template #footer>
+      <BaseButton
+        v-if="actionType !== 'detail'"
+        type="primary"
+        :loading="saveLoading"
+        @click="save"
+      >
+        {{ t('exampleDemo.save') }}
+      </BaseButton>
+      <BaseButton @click="dialogVisible = false">{{ t('dialogDemo.close') }}</BaseButton>
+    </template>
+  </Dialog>
+</template>

+ 13 - 0
src/views/Staff/designer/components/Write.vue

@@ -22,6 +22,10 @@ const props = defineProps({
   currentRow: {
     type: Object as PropType<any>,
     default: () => null
+  },
+  groupList: {
+    type: Array,
+    default: () => [{ label: '无', value: 0 }]
   }
 })
 const formSchema = ref<FormSchema[]>([
@@ -46,6 +50,15 @@ const formSchema = ref<FormSchema[]>([
       }
     }
   },
+  {
+    field: 'group_id',
+    label: '所属组',
+    component: 'Select',
+    componentProps: {
+      placeholder: '请选择组',
+      options: props.groupList
+    }
+  },
   {
     field: 'name',
     label: '姓名',

+ 62 - 29
src/views/Staff/designer/index.vue

@@ -1,6 +1,6 @@
 <script setup lang="tsx">
-import { reactive, ref, unref } from 'vue'
-import { getDesigner, addDesigner, putDesigner, delDesigner } from '@/api/staff'
+import { reactive, ref, unref, onMounted } from 'vue'
+import { getDesigner, addDesigner, putDesigner, delDesigner, getDesignerGroup } from '@/api/staff'
 import { useTable } from '@/hooks/web/useTable'
 import { useI18n } from '@/hooks/web/useI18n'
 import { Table, TableColumn } from '@/components/Table'
@@ -25,14 +25,17 @@ const { tableRegister, tableState, tableMethods } = useTable({
       ...unref(searchParams)
     })
     return {
-      list: res.data.list,
+      list: res.data.list.map((ree) => {
+        ree.tag_list_chs = ree.tag_list_chs.filter((fil) => fil.length > 0)
+        return ree
+      }),
       total: res.data.count || 0
     }
   }
 })
 const { dataList, loading, total, currentPage, pageSize } = tableState
 const { getList } = tableMethods
-
+const groupList = ref([])
 const tableColumns = reactive<TableColumn[]>([
   {
     field: 'id',
@@ -56,13 +59,29 @@ const tableColumns = reactive<TableColumn[]>([
   },
   {
     field: 'name',
-    label: '姓名',
-    minWidth: 100
+    label: '员工信息',
+    minWidth: 160,
+    slots: {
+      default: ({ row }: any) => {
+        return (
+          <>
+            姓名:{row.name}({row.gender_chs})<br></br>
+            手机:{row.phone}
+          </>
+        )
+      }
+    }
   },
   {
-    field: 'phone',
-    label: '手机号',
-    minWidth: 120
+    field: 'group',
+    label: '所属组',
+    align: 'center',
+    width: 100,
+    slots: {
+      default: ({ row }: any) => {
+        return <>{row.group ? row.group.name : ''}</>
+      }
+    }
   },
   {
     field: 'start_job_year',
@@ -71,13 +90,6 @@ const tableColumns = reactive<TableColumn[]>([
     headerAlign: 'center',
     minWidth: 100
   },
-  {
-    field: 'gender_chs',
-    align: 'center',
-    headerAlign: 'center',
-    label: '性别',
-    minWidth: 60
-  },
   {
     field: 'area',
     label: '区域',
@@ -173,6 +185,24 @@ const searchSchema = reactive<FormSchema[]>([
       ]
     }
   },
+  {
+    field: 'group_id',
+    label: '组',
+    component: 'Select',
+    componentProps: {
+      placeholder: '请选择要搜索的组'
+    },
+    optionApi: async () => {
+      const res = await getDesignerGroup({ page: 1, limit: 100 })
+      groupList.value = res.data.list.map((ree) => {
+        return {
+          value: ree.id,
+          label: ree.name
+        }
+      })
+      return groupList.value
+    }
+  },
   {
     field: 'tag_list',
     label: '标签',
@@ -182,6 +212,7 @@ const searchSchema = reactive<FormSchema[]>([
       placeholder: '请输入搜索标签'
     }
   },
+
   {
     field: 'name',
     label: '姓名',
@@ -223,11 +254,12 @@ const action = async (row: any, type: string) => {
     province: row.province,
     city: row.city,
     area: row.area,
-    tag_list: row.tag_list.split(','),
+    tag_list: row.tag_list,
     longitude: row.longitude,
     latitude: row.latitude,
     detail_address: row.detail_address,
-    start_job_year: row.start_job_year
+    start_job_year: row.start_job_year,
+    group_id: row.group_id == 0 ? '' : row.group_id
   }
   dialogVisible.value = true
 }
@@ -254,11 +286,14 @@ const save = async () => {
       province: formData.province,
       city: formData.city,
       area: formData.area,
-      tag_list: formData.tag_list,
       longitude: formData.longitude,
       latitude: formData.latitude,
       detail_address: formData.detail_address,
-      start_job_year: formData.start_job_year
+      start_job_year: formData.start_job_year,
+      group_id: !formData.group_id ? 0 : formData.group_id
+    }
+    if (formData.tag_list) {
+      data.tag_list = formData.tag_list.filter((fil) => fil.length > 0)
     }
     try {
       let res: any = {}
@@ -308,9 +343,7 @@ const delAction = (row: any) => {
 
 <template>
   <ContentWrap>
-    <div class="searchBox">
-      <Search :schema="searchSchema" @reset="setSearchParams" @search="setSearchParams" />
-    </div>
+    <Search :schema="searchSchema" @reset="setSearchParams" @search="setSearchParams" />
     <div class="mb-10px">
       <BaseButton type="primary" @click="AddAction">{{ t('exampleDemo.add') }}</BaseButton>
     </div>
@@ -331,7 +364,12 @@ const delAction = (row: any) => {
   </ContentWrap>
 
   <Dialog v-model="dialogVisible" :title="dialogTitle" width="900px" max-height="700px">
-    <Write v-if="actionType !== 'detail'" ref="writeRef" :current-row="currentRow" />
+    <Write
+      :groupList="groupList"
+      v-if="actionType !== 'detail'"
+      ref="writeRef"
+      :current-row="currentRow"
+    />
     <template #footer>
       <BaseButton
         v-if="actionType !== 'detail'"
@@ -345,8 +383,3 @@ const delAction = (row: any) => {
     </template>
   </Dialog>
 </template>
-<style lang="less">
-.searchBox {
-  max-width: 1000px;
-}
-</style>

+ 1 - 2
src/views/Staff/group/components/designerWrite.vue

@@ -5,7 +5,7 @@ import { PropType, watch, ref } from 'vue'
 import { useValidator } from '@/hooks/web/useValidator'
 import { cloneDeep } from 'lodash-es'
 import { getCity } from '@/api/system/admin'
-import { DesignerButtom } from '@/components/designerList'
+import { DesignerButtom } from '@/components/DesignerList'
 const { required } = useValidator()
 
 const props = defineProps({
@@ -141,7 +141,6 @@ watch(
   async (value) => {
     if (!value) return
     const currentRow = cloneDeep(value)
-    console.log(currentRow, 'currentRow')
     setValues(currentRow)
   },
   {

+ 64 - 286
src/views/Staff/group/components/workerWrite.vue

@@ -1,144 +1,72 @@
 <script setup lang="tsx">
 import { Form, FormSchema } from '@/components/Form'
 import { useForm } from '@/hooks/web/useForm'
-import { PropType, watch, ref, onMounted, unref } from 'vue'
+import { PropType, watch, ref } from 'vue'
 import { useValidator } from '@/hooks/web/useValidator'
 import { cloneDeep } from 'lodash-es'
-import { ElInputTag, ElAutocomplete } from 'element-plus'
-import { getMapSearch } from '@/api/system/admin'
-import { UpImgButtom } from '@/components/UpFile'
-import { mapAddressData } from '@/api/system/admin/types'
-import { getConfigKey } from '@/api/system/admin'
-import { UserButtom } from '@/components/UserList'
-const { required, phone } = useValidator()
-const mapKey = ref('')
-onMounted(async () => {
-  const res = await getConfigKey('tengxun_map_key')
-  if (res) {
-    mapKey.value = res.data.tengxun_map_key
-  }
-})
+import { getCity } from '@/api/system/admin'
+import { WorkerButtom } from '@/components/WorkerList'
+const { required } = useValidator()
+
 const props = defineProps({
   currentRow: {
     type: Object as PropType<any>,
     default: () => null
   }
 })
-const formSchema = ref<FormSchema[]>([
-  {
-    field: 'uid',
-    label: '用户',
-    component: 'CheckboxGroup',
-    formItemProps: {
-      slots: {
-        default: (data) => {
-          return (
-            <>
-              <UserButtom
-                v-model={data.uid}
-                onChangeUser={(res) => {
-                  checkedUser(res)
-                }}
-              ></UserButtom>
-            </>
-          )
-        }
-      }
+const getCityList = async (node, resolve) => {
+  const { value } = node
+  const res = await getCity({ pid: value || 0, type: 2 })
+  const data = res.data.map((ree) => {
+    return {
+      label: ree.label,
+      value: ree.value,
+      leaf: !ree.children
     }
-  },
+  })
+  resolve(data)
+}
+const cityChange = (value) => {
+  console.log(value, 'value')
+  setValues({
+    province: value[0],
+    city: value[1],
+    area: value[2]
+  })
+}
+const checked = (res) => {
+  // console.log(res, 'res')
+  setValues({
+    group_leader_id: res.id
+  })
+}
+const formSchema = ref<FormSchema[]>([
   {
     field: 'name',
-    label: '姓名',
+    label: '名',
     component: 'Input',
-    componentProps: {
-      placeholder: '请输入姓名'
-    }
-  },
-  {
-    field: 'avatar',
-    label: '头像',
-    component: 'CheckboxGroup',
-    componentProps: {
-      placeholder: '选择头像'
+    colProps: {
+      span: 24
     },
-    formItemProps: {
-      slots: {
-        default: (data) => {
-          return (
-            <>
-              <UpImgButtom v-model={data.avatar}></UpImgButtom>
-            </>
-          )
-        }
-      }
-    }
-  },
-  {
-    field: 'phone',
-    label: '手机号',
-    component: 'Input',
     componentProps: {
-      type: 'number',
-      placeholder: '请输入手机号'
-    }
-  },
-  {
-    field: 'gender',
-    label: '性别',
-    component: 'Select',
-    value: 0,
-    componentProps: {
-      placeholder: '请选择性别',
-      options: [
-        {
-          label: '保密',
-          value: 0
-        },
-        {
-          label: '男',
-          value: 1
-        },
-        {
-          label: '女',
-          value: 2
-        }
-      ]
+      placeholder: '请输入组名称'
     }
   },
-
-  {
-    field: 'birth_day_time',
-    label: '出生日期',
-    component: 'DatePicker'
-  },
-
   {
-    field: 'start_job_year',
-    label: '就业年份',
-    component: 'Input',
-    componentProps: {
-      placeholder: '例子:2014'
-    }
-  },
-
-  {
-    field: 'tag_list',
-    label: '标签',
-    colProps: {
-      span: 24
-    },
+    field: 'group_leader_id',
+    label: '组长',
     component: 'CheckboxGroup',
     formItemProps: {
       slots: {
         default: (data) => {
           return (
             <>
-              <ElInputTag
-                v-model={data.tag_list}
-                tagEffect="light"
-                tagType="primary"
-                clearable
-              ></ElInputTag>
+              <WorkerButtom
+                v-model={data.id}
+                onChange={(res) => {
+                  checked(res)
+                }}
+              ></WorkerButtom>
             </>
           )
         }
@@ -146,44 +74,21 @@ const formSchema = ref<FormSchema[]>([
     }
   },
   {
-    field: 'detail_address',
-    label: '省市区地址',
+    field: 'address',
+    label: '地址选择',
+    component: 'Cascader',
     colProps: {
       span: 24
     },
-    component: 'Input',
-    formItemProps: {
-      slots: {
-        default: (data) => {
-          return (
-            <>
-              <ElAutocomplete
-                v-model={data.detail_address}
-                value-key="label"
-                onSelect={(val: any) => {
-                  selectAddress(val)
-                }}
-                fetch-suggestions={(val, cb) => {
-                  searchAddress(val, cb)
-                }}
-              >
-                {{
-                  default: ({ item }) => {
-                    return (
-                      <>
-                        <div class="lh-none mt-10px">{item.label}</div>
-                        <div class="font-size-12px lh-none  mt-5px pb-10px b-b-solid b-b-light b-b-1px">
-                          {item.address}
-                        </div>
-                      </>
-                    )
-                  }
-                }}
-              </ElAutocomplete>
-            </>
-          )
-        }
-      }
+    componentProps: {
+      on: {
+        change: cityChange
+      },
+      props: {
+        lazy: true,
+        lazyLoad: getCityList
+      },
+      placeholder: '请输入组名称'
     }
   },
   {
@@ -204,44 +109,17 @@ const formSchema = ref<FormSchema[]>([
     hidden: true,
     component: 'Input'
   },
-  {
-    field: 'longitude',
-    label: '经度',
-    hidden: true,
-    component: 'Input'
-  },
-  {
-    field: 'latitude',
-    label: '纬度',
-    hidden: true,
-    component: 'Input'
-  },
   {
     field: 'id',
+    label: 'id',
     hidden: true,
     component: 'Input'
   }
 ])
 
 const rules = ref({
-  uid: [required('请选择用户')],
-  avatar: [required('请上传头像')],
-  phone: [required('请填写手机号'), phone()],
-  birth_day_time: [required('请选择出生日期')],
-  start_job_year: [required('请填写就业年份')],
-  detail_address: [
-    {
-      required: true,
-      validator: (_, val, callback) => {
-        if (!actionAddress.value || !val) {
-          callback(new Error('请选择地址'))
-        } else {
-          callback()
-        }
-      }
-    }
-  ],
-  name: [required('请填写等级名称')]
+  name: [required('请填写组名称')],
+  group_leader_id: [required('请选择设计师')]
 })
 
 const { formRegister, formMethods } = useForm()
@@ -258,82 +136,12 @@ const submit = async () => {
   }
 }
 
-defineExpose({
-  submit
-})
-const checkedUser = async (res: any) => {
-  setValues({
-    uid: res.uid,
-    name: res.real_name,
-    avatar: [res.avatar],
-    birth_day_time: res.birthday,
-    phone: res.phone,
-    gender: res.sex || 0
-  })
-}
-const actionAddress = ref<mapAddressData>()
-const searchAddress = async (queryString: string, cb: (arg: any) => void) => {
-  if (queryString) {
-    const { data } = await getMapSearch({
-      key: unref(mapKey),
-      keyword: queryString
-    })
-    cb(
-      data.map((item): mapAddressData => {
-        return {
-          adcode: item.adcode,
-          label: item.title,
-          address: item.address,
-          lng: item.location.lng,
-          lat: item.location.lat,
-          province: item.province,
-          city: item.city,
-          district: item.district
-        }
-      })
-    )
-  }
-}
-const selectAddress = (item: mapAddressData) => {
-  if (!item || !item.adcode || !item.lng || !item.lat) {
-    console.error('Invalid input item:', item)
-    return
-  }
-
-  const adcodeStr = String(item.adcode).padStart(6, '0') // 确保 adcode 至少有6位
-  const province = adcodeStr.slice(0, 2)
-  const city = adcodeStr.slice(2, 4)
-  const area = adcodeStr
-
-  setValues({
-    province: `${province}`,
-    city: `${province}${city}00000000`,
-    area: `${area}000000`,
-    longitude: item.lng,
-    latitude: item.lat
-  })
-
-  actionAddress.value = item
-  center.value = {
-    lng: item.lng,
-    lat: item.lat
-  }
-  geometries.value = [{ styleId: 'marker', position: { lat: item.lat, lng: item.lng } }]
-}
-const center = ref({ lat: 28.655759, lng: 121.420808 })
-const geometries = ref([{ styleId: 'marker', position: { lat: 28.655759, lng: 121.420808 } }])
-
 watch(
   () => props.currentRow,
   async (value) => {
     if (!value) return
     const currentRow = cloneDeep(value)
-    actionAddress.value = {
-      adcode: currentRow.area,
-      label: currentRow.detail_address,
-      lng: currentRow.longitude,
-      lat: currentRow.latitude
-    }
+    console.log(currentRow, 'currentRow')
     setValues(currentRow)
   },
   {
@@ -341,42 +149,12 @@ watch(
     immediate: true
   }
 )
+
+defineExpose({
+  submit
+})
 </script>
 
 <template>
-  <div class="flex flex-col">
-    <Form :rules="rules" @register="formRegister" :schema="formSchema" />
-    <TlbsMap
-      v-if="mapKey"
-      ref="map"
-      :api-key="mapKey"
-      :zoom="15"
-      :center="center"
-      :control="{
-        scale: {},
-        zoom: {
-          position: 'topRight'
-        }
-      }"
-    >
-      <TlbsMultiMarker
-        :geometries="geometries"
-        :styles="{
-          marker: {
-            width: 20,
-            height: 30,
-            anchor: { x: 10, y: 30 }
-          }
-        }"
-      />
-    </TlbsMap>
-  </div>
+  <Form :rules="rules" @register="formRegister" :schema="formSchema" />
 </template>
-<style lang="less" scoped>
-:deep(.el-table__row) {
-  .list {
-    width: 50px;
-    text-align: center;
-  }
-}
-</style>

+ 8 - 9
src/views/Staff/group/designer.vue

@@ -46,7 +46,13 @@ const tableColumns = reactive<TableColumn[]>([
   {
     field: 'group_leader_id',
     label: '组长',
-    minWidth: 120
+    minWidth: 120,
+    slots: {
+      default: (data: any) => {
+        const row = data.row
+        return row.group_leader_id ? row.leader.name : '暂无'
+      }
+    }
   },
   {
     field: 'action',
@@ -183,9 +189,7 @@ const delAction = (row: any) => {
 
 <template>
   <ContentWrap>
-    <div class="searchBox">
-      <Search :schema="searchSchema" @reset="setSearchParams" @search="setSearchParams" />
-    </div>
+    <Search :schema="searchSchema" @reset="setSearchParams" @search="setSearchParams" />
     <div class="mb-10px">
       <BaseButton type="primary" @click="AddAction">{{ t('exampleDemo.add') }}</BaseButton>
     </div>
@@ -220,8 +224,3 @@ const delAction = (row: any) => {
     </template>
   </Dialog>
 </template>
-<style lang="less">
-.searchBox {
-  max-width: 1000px;
-}
-</style>

+ 20 - 111
src/views/Staff/group/worker.vue

@@ -11,8 +11,7 @@ import Write from './components/workerWrite.vue'
 import { BaseButton } from '@/components/Button'
 import { ElMessage, ElDivider, ElMessageBox } from 'element-plus'
 import { Dialog } from '@/components/Dialog'
-import { designerData, designerSearch } from '@/api/staff/types'
-import { formatToDate } from '@/utils/dateUtil'
+import { staffGroupSearch, staffGroupData } from '@/api/staff/types'
 const { t } = useI18n()
 
 const { tableRegister, tableState, tableMethods } = useTable({
@@ -41,49 +40,20 @@ const tableColumns = reactive<TableColumn[]>([
   },
   {
     field: 'name',
-    label: '组名',
+    label: '组名',
     minWidth: 100
   },
   {
-    field: 'phone',
-    label: '手机号',
-    minWidth: 120
-  },
-  {
-    field: 'province',
-    label: '省',
-    align: 'center',
-    headerAlign: 'center',
-    minWidth: 100
-  },
-  {
-    field: 'city',
-    align: 'center',
-    headerAlign: 'center',
-    label: '市',
-    minWidth: 60
-  },
-  {
-    field: 'area',
-    align: 'center',
-    headerAlign: 'center',
-    label: '区',
-    minWidth: 60
+    field: 'group_leader_id',
+    label: '组长',
+    minWidth: 120,
+    slots: {
+      default: (data: any) => {
+        const row = data.row
+        return row.group_leader_id ? row.leader.name : '暂无'
+      }
+    }
   },
-  // {
-  //   field: 'area',
-  //   label: '区域',
-  //   minWidth: 200,
-  //   slots: {·
-  //     default: ({ row }: any) => {
-  //       return (
-  //         <>
-  //           {row.province_text} {row.city_text} {row.area_text}
-  //         </>
-  //       )
-  //     }
-  //   }
-  // },
   {
     field: 'action',
     label: t('userDemo.action'),
@@ -111,45 +81,9 @@ const tableColumns = reactive<TableColumn[]>([
 ])
 
 const searchSchema = reactive<FormSchema[]>([
-  {
-    field: 'gender',
-    label: '性别',
-    component: 'Select',
-    value: '',
-    componentProps: {
-      placeholder: '性别',
-      options: [
-        {
-          label: '全部',
-          value: ''
-        },
-        {
-          label: '保密',
-          value: 0
-        },
-        {
-          label: '男',
-          value: 1
-        },
-        {
-          label: '女',
-          value: 2
-        }
-      ]
-    }
-  },
-  {
-    field: 'tag_list',
-    label: '标签',
-    component: 'Input',
-    value: '',
-    componentProps: {
-      placeholder: '请输入搜索标签'
-    }
-  },
   {
     field: 'name',
-    label: '名',
+    label: '组名',
     component: 'Input',
     value: '',
     componentProps: {
@@ -158,9 +92,7 @@ const searchSchema = reactive<FormSchema[]>([
   }
 ])
 
-const searchParams = ref<designerSearch>({
-  gender: '',
-  tag_list: '',
+const searchParams = ref<staffGroupSearch>({
   name: ''
 })
 const setSearchParams = (data: any) => {
@@ -179,20 +111,12 @@ const action = async (row: any, type: string) => {
   actionType.value = type
   currentRow.value = {
     name: row.name,
-    uid: row.uid,
     id: row.id,
-    phone: row.phone,
-    avatar: [row.avatar],
-    gender: row.gender,
-    birth_day_time: formatToDate(row.birth_day_time * 1000),
     province: row.province,
     city: row.city,
     area: row.area,
-    tag_list: row.tag_list.split(','),
-    longitude: row.longitude,
-    latitude: row.latitude,
-    detail_address: row.detail_address,
-    start_job_year: row.start_job_year
+    group_leader_id: row.group_leader_id,
+    address: [parseInt(row.province), parseInt(row.city), parseInt(row.area)]
   }
   dialogVisible.value = true
 }
@@ -209,21 +133,13 @@ const save = async () => {
   const formData = await write?.submit()
   if (formData) {
     saveLoading.value = true
-    const data: designerData = {
+    const data: staffGroupData = {
       name: formData.name,
-      uid: formData.uid,
-      phone: formData.phone,
-      avatar: formData.avatar[0],
-      gender: formData.gender,
-      birth_day_time: formatToDate(formData.birth_day_time),
+      id: formData.id,
       province: formData.province,
       city: formData.city,
       area: formData.area,
-      tag_list: formData.tag_list,
-      longitude: formData.longitude,
-      latitude: formData.latitude,
-      detail_address: formData.detail_address,
-      start_job_year: formData.start_job_year
+      group_leader_id: formData.group_leader_id
     }
     try {
       let res: any = {}
@@ -273,9 +189,7 @@ const delAction = (row: any) => {
 
 <template>
   <ContentWrap>
-    <div class="searchBox">
-      <Search :schema="searchSchema" @reset="setSearchParams" @search="setSearchParams" />
-    </div>
+    <Search :schema="searchSchema" @reset="setSearchParams" @search="setSearchParams" />
     <div class="mb-10px">
       <BaseButton type="primary" @click="AddAction">{{ t('exampleDemo.add') }}</BaseButton>
     </div>
@@ -295,7 +209,7 @@ const delAction = (row: any) => {
     />
   </ContentWrap>
 
-  <Dialog v-model="dialogVisible" :title="dialogTitle" width="900px" max-height="700px">
+  <Dialog v-model="dialogVisible" :title="dialogTitle" width="500px">
     <Write v-if="actionType !== 'detail'" ref="writeRef" :current-row="currentRow" />
     <template #footer>
       <BaseButton
@@ -310,8 +224,3 @@ const delAction = (row: any) => {
     </template>
   </Dialog>
 </template>
-<style lang="less">
-.searchBox {
-  max-width: 1000px;
-}
-</style>

+ 84 - 2
src/views/Staff/salesman/components/Write.vue

@@ -10,6 +10,7 @@ import { UpImgButtom } from '@/components/UpFile'
 import { mapAddressData } from '@/api/system/admin/types'
 import { getConfigKey } from '@/api/system/admin'
 import { UserButtom } from '@/components/UserList'
+import { SalespersonButtom } from '@/components/SalespersonList'
 const { required, phone } = useValidator()
 const mapKey = ref('')
 onMounted(async () => {
@@ -46,6 +47,27 @@ const formSchema = ref<FormSchema[]>([
       }
     }
   },
+  {
+    field: 'parent_id',
+    label: '邀请人',
+    component: 'CheckboxGroup',
+    formItemProps: {
+      slots: {
+        default: (data) => {
+          return (
+            <>
+              <SalespersonButtom
+                v-model={data.parent_id}
+                onChange={(res) => {
+                  checkedPareent(res)
+                }}
+              ></SalespersonButtom>
+            </>
+          )
+        }
+      }
+    }
+  },
   {
     field: 'name',
     label: '姓名',
@@ -73,6 +95,52 @@ const formSchema = ref<FormSchema[]>([
       }
     }
   },
+  {
+    field: 'salesperson_status',
+    label: '自定义分成',
+    value: false,
+    component: 'Switch',
+    componentProps: {
+      on: {
+        change: (val) => {
+          if (val) {
+            setValues({
+              salesperson_commission: 0
+            })
+            setSchema([
+              {
+                field: 'salesperson_commission',
+                path: 'componentProps.disabled',
+                value: false
+              }
+            ])
+          } else {
+            setValues({
+              salesperson_commission: '默认'
+            })
+            setSchema([
+              {
+                field: 'salesperson_commission',
+                path: 'componentProps.disabled',
+                value: true
+              }
+            ])
+          }
+        }
+      }
+    }
+  },
+  {
+    field: 'salesperson_commission',
+    label: '分成(%)',
+    value: '默认',
+    component: 'Input',
+    componentProps: {
+      disabled: true,
+      type: 'text',
+      placeholder: '请输入分成比例'
+    }
+  },
   {
     field: 'phone',
     label: '手机号',
@@ -82,7 +150,6 @@ const formSchema = ref<FormSchema[]>([
       placeholder: '请输入手机号'
     }
   },
-
   {
     field: 'detail_address',
     label: '省市区地址',
@@ -181,7 +248,7 @@ const rules = ref({
 })
 
 const { formRegister, formMethods } = useForm()
-const { setValues, getFormData, getElFormExpose } = formMethods
+const { setValues, getFormData, getElFormExpose, setSchema } = formMethods
 
 const submit = async () => {
   const elForm = await getElFormExpose()
@@ -207,6 +274,12 @@ const checkedUser = async (res: any) => {
     gender: res.sex || 0
   })
 }
+const checkedPareent = async (res: any) => {
+  setValues({
+    parent_id: res.id
+  })
+}
+
 const actionAddress = ref<mapAddressData>()
 const searchAddress = async (queryString: string, cb: (arg: any) => void) => {
   if (queryString) {
@@ -270,6 +343,15 @@ watch(
       lng: currentRow.longitude,
       lat: currentRow.latitude
     }
+    if (currentRow.salesperson_status) {
+      setSchema([
+        {
+          field: 'salesperson_commission',
+          path: 'componentProps.disabled',
+          value: false
+        }
+      ])
+    }
     setValues(currentRow)
   },
   {

+ 10 - 10
src/views/Staff/salesman/index.vue

@@ -153,7 +153,11 @@ const action = async (row: any, type: string) => {
     area: row.area,
     longitude: row.longitude,
     latitude: row.latitude,
-    detail_address: row.detail_address
+    detail_address: row.detail_address,
+    parent_id: row.parent_id,
+    salesperson_status: row.salesperson_commission === '-1.00' ? false : true,
+    salesperson_commission:
+      row.salesperson_commission === '-1.00' ? '默认' : row.salesperson_commission
   }
   dialogVisible.value = true
 }
@@ -180,7 +184,10 @@ const save = async () => {
       area: formData.area,
       longitude: formData.longitude,
       latitude: formData.latitude,
-      detail_address: formData.detail_address
+      detail_address: formData.detail_address,
+      parent_id: formData.parent_id,
+      salesperson_commission:
+        formData.salesperson_commission === '默认' ? '-1.00' : formData.salesperson_commission
     }
     try {
       let res: any = {}
@@ -230,9 +237,7 @@ const delAction = (row: any) => {
 
 <template>
   <ContentWrap>
-    <div class="searchBox">
-      <Search :schema="searchSchema" @reset="setSearchParams" @search="setSearchParams" />
-    </div>
+    <Search :schema="searchSchema" @reset="setSearchParams" @search="setSearchParams" />
     <div class="mb-10px">
       <BaseButton type="primary" @click="AddAction">{{ t('exampleDemo.add') }}</BaseButton>
     </div>
@@ -267,8 +272,3 @@ const delAction = (row: any) => {
     </template>
   </Dialog>
 </template>
-<style lang="less">
-.searchBox {
-  max-width: 1000px;
-}
-</style>

+ 13 - 0
src/views/Staff/worker/components/Write.vue

@@ -23,6 +23,10 @@ const props = defineProps({
   currentRow: {
     type: Object as PropType<any>,
     default: () => null
+  },
+  groupList: {
+    type: Array,
+    default: () => [{ label: '无', value: 0 }]
   }
 })
 const formSchema = ref<FormSchema[]>([
@@ -47,6 +51,15 @@ const formSchema = ref<FormSchema[]>([
       }
     }
   },
+  {
+    field: 'group_id',
+    label: '所属组',
+    component: 'Select',
+    componentProps: {
+      placeholder: '请选择组',
+      options: props.groupList
+    }
+  },
   {
     field: 'name',
     label: '姓名',

+ 68 - 37
src/views/Staff/worker/index.vue

@@ -1,6 +1,6 @@
 <script setup lang="tsx">
-import { reactive, ref, unref } from 'vue'
-import { getWorker, addWorker, putWorker, delWorker } from '@/api/staff'
+import { reactive, ref, unref, onMounted } from 'vue'
+import { getWorker, addWorker, putWorker, delWorker, getWorkerGroup } from '@/api/staff'
 import { useTable } from '@/hooks/web/useTable'
 import { useI18n } from '@/hooks/web/useI18n'
 import { Table, TableColumn } from '@/components/Table'
@@ -25,14 +25,17 @@ const { tableRegister, tableState, tableMethods } = useTable({
       ...unref(searchParams)
     })
     return {
-      list: res.data.list,
+      list: res.data.list.map((ree) => {
+        ree.tag_list_chs = ree.tag_list_chs.filter((fil) => fil.length > 0)
+        return ree
+      }),
       total: res.data.count || 0
     }
   }
 })
 const { dataList, loading, total, currentPage, pageSize } = tableState
 const { getList } = tableMethods
-
+const groupList = ref([])
 const tableColumns = reactive<TableColumn[]>([
   {
     field: 'id',
@@ -43,6 +46,7 @@ const tableColumns = reactive<TableColumn[]>([
   {
     field: 'avatar',
     label: '头像',
+    headerAlign: 'center',
     width: 80,
     slots: {
       default: ({ row }: any) => {
@@ -56,27 +60,36 @@ const tableColumns = reactive<TableColumn[]>([
   },
   {
     field: 'name',
-    label: '姓名',
-    minWidth: 100
+    label: '员工信息',
+    minWidth: 160,
+    slots: {
+      default: ({ row }: any) => {
+        return (
+          <>
+            姓名:{row.name}({row.gender_chs})<br></br>
+            手机:{row.phone}
+          </>
+        )
+      }
+    }
   },
   {
-    field: 'phone',
-    label: '手机号',
-    minWidth: 120
+    field: 'group',
+    label: '所属组',
+    align: 'center',
+    width: 100,
+    slots: {
+      default: ({ row }: any) => {
+        return <>{row.group ? row.group.name : ''}</>
+      }
+    }
   },
   {
     field: 'start_job_year',
     label: '就业年份',
     align: 'center',
     headerAlign: 'center',
-    minWidth: 100
-  },
-  {
-    field: 'gender_chs',
-    align: 'center',
-    headerAlign: 'center',
-    label: '性别',
-    minWidth: 60
+    width: 100
   },
   {
     field: 'area',
@@ -87,17 +100,13 @@ const tableColumns = reactive<TableColumn[]>([
         return (
           <>
             {row.province_text} {row.city_text} {row.area_text}
+            <br></br>
+            {row.detail_address}
           </>
         )
       }
     }
   },
-  {
-    field: 'detail_address',
-    label: '定位',
-    minWidth: 200
-  },
-
   {
     field: 'tag_list_chs',
     label: '标签',
@@ -172,6 +181,24 @@ const searchSchema = reactive<FormSchema[]>([
       ]
     }
   },
+  {
+    field: 'group_id',
+    label: '组',
+    component: 'Select',
+    componentProps: {
+      placeholder: '请选择要搜索的组'
+    },
+    optionApi: async () => {
+      const res = await getWorkerGroup({ page: 1, limit: 100 })
+      groupList.value = res.data.list.map((ree) => {
+        return {
+          value: ree.id,
+          label: ree.name
+        }
+      })
+      return groupList.value
+    }
+  },
   {
     field: 'tag_list',
     label: '标签',
@@ -195,7 +222,8 @@ const searchSchema = reactive<FormSchema[]>([
 const searchParams = ref<designerSearch>({
   gender: '',
   tag_list: '',
-  name: ''
+  name: '',
+  group_id: ''
 })
 const setSearchParams = (data: any) => {
   searchParams.value = data
@@ -222,11 +250,12 @@ const action = async (row: any, type: string) => {
     province: row.province,
     city: row.city,
     area: row.area,
-    tag_list: row.tag_list.split(','),
+    tag_list: row.tag_list,
     longitude: row.longitude,
     latitude: row.latitude,
     detail_address: row.detail_address,
-    start_job_year: row.start_job_year
+    start_job_year: row.start_job_year,
+    group_id: row.group_id == 0 ? '' : row.group_id
   }
   dialogVisible.value = true
 }
@@ -243,6 +272,7 @@ const save = async () => {
   const formData = await write?.submit()
   if (formData) {
     saveLoading.value = true
+    console.log(formData.tag_list, 'formData.tag_list')
     const data: designerData = {
       name: formData.name,
       uid: formData.uid,
@@ -253,11 +283,14 @@ const save = async () => {
       province: formData.province,
       city: formData.city,
       area: formData.area,
-      tag_list: formData.tag_list,
       longitude: formData.longitude,
       latitude: formData.latitude,
       detail_address: formData.detail_address,
-      start_job_year: formData.start_job_year
+      start_job_year: formData.start_job_year,
+      group_id: !formData.group_id ? 0 : formData.group_id
+    }
+    if (formData.tag_list) {
+      data.tag_list = formData.tag_list.filter((fil) => fil.length > 0)
     }
     try {
       let res: any = {}
@@ -307,9 +340,7 @@ const delAction = (row: any) => {
 
 <template>
   <ContentWrap>
-    <div class="searchBox">
-      <Search :schema="searchSchema" @reset="setSearchParams" @search="setSearchParams" />
-    </div>
+    <Search :schema="searchSchema" @reset="setSearchParams" @search="setSearchParams" />
     <div class="mb-10px">
       <BaseButton type="primary" @click="AddAction">{{ t('exampleDemo.add') }}</BaseButton>
     </div>
@@ -330,7 +361,12 @@ const delAction = (row: any) => {
   </ContentWrap>
 
   <Dialog v-model="dialogVisible" :title="dialogTitle" width="900px" max-height="700px">
-    <Write v-if="actionType !== 'detail'" ref="writeRef" :current-row="currentRow" />
+    <Write
+      :groupList="groupList"
+      v-if="actionType !== 'detail'"
+      ref="writeRef"
+      :current-row="currentRow"
+    />
     <template #footer>
       <BaseButton
         v-if="actionType !== 'detail'"
@@ -344,8 +380,3 @@ const delAction = (row: any) => {
     </template>
   </Dialog>
 </template>
-<style lang="less">
-.searchBox {
-  max-width: 1000px;
-}
-</style>

+ 2 - 0
src/views/System/log/index.vue

@@ -155,6 +155,8 @@ const searchParams = ref<{
 const setSearchParams = (data: any) => {
   if (data.time) {
     data.time = data.time.join(' - ')
+  } else {
+    data.time = ''
   }
   searchParams.value = data
   getList()

+ 1 - 6
src/views/User/level/index.vue

@@ -320,7 +320,7 @@ const changeLevelStatus = (item) => {
 
 <template>
   <ContentWrap>
-    <div class="searchBox">
+    <div class="max-w-1000px">
       <Search :schema="searchSchema" @reset="setSearchParams" @search="setSearchParams" />
     </div>
     <div class="mb-10px">
@@ -357,8 +357,3 @@ const changeLevelStatus = (item) => {
     </template>
   </Dialog>
 </template>
-<style lang="less">
-.searchBox {
-  max-width: 1000px;
-}
-</style>

+ 52 - 41
src/views/User/list/components/DetailFrom.vue

@@ -84,6 +84,7 @@ const userRecordList = ref([
     total: 0,
     data: [],
     loading: true,
+    initLoading: true,
     columns: [
       {
         label: '时间',
@@ -114,6 +115,7 @@ const userRecordList = ref([
     limit: 10,
     total: 0,
     loading: true,
+    initLoading: true,
     data: [],
     columns: [
       {
@@ -145,6 +147,7 @@ const userRecordList = ref([
     limit: 10,
     total: 100,
     loading: true,
+    initLoading: true,
     data: [],
     columns: [
       {
@@ -169,52 +172,59 @@ const userRecordList = ref([
 const changeTable = (row: any) => {
   for (let index = 0; index < userRecordList.value.length; index++) {
     const element = userRecordList.value[index]
-    if (row == element.type) {
-      if (element.loading) {
-        if (element.type == '1') {
-          getSpreadList(
-            {
-              page: element.page,
-              limit: element.limit
-            },
-            props.userDetail.uid
-          ).then((res) => {
-            element.data = res.data.list
-            element.total = res.data.total
-            element.loading = false
-          })
-        } else if (element.type == '2') {
-          getBalanceLog(
-            {
-              page: element.page,
-              limit: element.limit
-            },
-            props.userDetail.uid as number
-          ).then((res) => {
-            element.data = res.data.data
-            element.total = res.data.count
-            element.loading = false
-          })
-        } else if (element.type == '3') {
-          getBrokerageLog(
-            {
-              page: element.page,
-              limit: element.limit
-            },
-            props.userDetail.uid
-          ).then((res) => {
-            element.data = res.data.data
-            element.total = res.data.count
-            element.loading = false
-          })
-        }
-      }
+    if (row == element.type && element.initLoading) {
+      getList(element)
+      element.initLoading = false
       break
     }
   }
 }
+const getList = async (element) => {
+  try {
+    if (!element.loading || element.initLoading) {
+      element.loading = true
+      if (element.type == '1') {
+        const res = await getSpreadList(
+          {
+            page: element.page,
+            limit: element.limit
+          },
+          props.userDetail.uid
+        )
+        element.data = res.data.list
+        element.total = res.data.total
+      } else if (element.type == '2') {
+        const res = await getBalanceLog(
+          {
+            page: element.page,
+            limit: element.limit
+          },
+          props.userDetail.uid as number
+        )
+        element.data = res.data.data
+        element.total = res.data.count
+      } else if (element.type == '3') {
+        const res = await getBrokerageLog(
+          {
+            page: element.page,
+            limit: element.limit
+          },
+          props.userDetail.uid
+        )
+        element.data = res.data.data
+        element.total = res.data.count
+      }
+      element.loading = false
+    }
+  } catch (err) {
+    console.log(err, 'err')
+    element.loading = false
+    element.initLoading = true
+  }
+}
 const changePage = (row: any, item) => {
-  console.log(row, item, 'changePage')
+  item.page = row
+  getList(item)
 }
 const getFormData = () => {
   const formData: userData = {
@@ -267,6 +277,7 @@ watch(
     for (let i = 0; i < ar.length; i++) {
       const it = ar[i]
       it.loading = true
+      it.initLoading = true
       it.page = 1
     }
   },

+ 3 - 5
src/views/User/list/index.vue

@@ -318,6 +318,8 @@ const searchParams = ref<userSearch>({
 const setSearchParams = (data: any) => {
   if (data.time) {
     data.time = data.time.join(' - ')
+  } else {
+    data.time = ''
   }
   searchParams.value = data
   getList()
@@ -451,7 +453,7 @@ watch(currentPage, (val) => {
 
 <template>
   <ContentWrap>
-    <div class="searchBox">
+    <div class="max-w-1000px">
       <Search
         :schema="searchSchema"
         @reset="setSearchParams"
@@ -557,10 +559,6 @@ watch(currentPage, (val) => {
   </Dialog>
 </template>
 <style lang="less">
-.searchBox {
-  max-width: 1000px;
-}
-
 .example-showcase .el-dropdown-link {
   display: flex;
   color: var(--el-color-primary);