فهرست منبع

add 客户管理接口对接

haveyou 1 سال پیش
والد
کامیت
3d75019dc7

+ 0 - 7
src/utils/request.js

@@ -63,13 +63,6 @@ service.interceptors.response.use(
       }
       return Promise.reject(new Error(res.data.msg));
     }
-    let bool =
-      res.data?.code !== 200 &&
-      res.config.method !== 'get' &&
-      res.config.responseType !== 'blob' && res.config.showTips !== false;
-    if (bool) {
-      EleMessage.error(res.data?.msg);
-    }
     return res;
   },
   (error) => {

+ 16 - 13
src/views/customer/blackMobile/components/page-edit.vue

@@ -23,28 +23,31 @@
         type: 'input',
         label: '手机号',
         prop: 'mobile',
-        required: true,
-        rules: [{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号' }]
+        itemProps: {
+          rules: [
+            { required: true, message: '请输入手机号', trigger: ['blur'] },
+            {
+              pattern: /^1[3-9]\d{9}$/,
+              message: '请输入正确的手机号',
+              trigger: ['change']
+            }
+          ]
+        }
       },
-      {
-        type: 'textarea',
-        label: '拉黑原因',
-        prop: 'reason',
-        required: true
-      }
+      { type: 'textarea', label: '拉黑原因', prop: 'reason', required: true }
     ];
   });
   //默认值
   const baseUrl = reactive({
-    add: '/baseinfo/schoolInfo/save',
-    update: '/baseinfo/schoolInfo/edit'
+    add: '/user/mobileBlack/add',
+    update: '/user/mobileBlack/update'
   });
-  const formData = ref({ schoolTag: '1' });
+  const formData = ref({});
 
   const editRef = ref(null);
 
-  function handleOpen(data = {}) {
-    formData.value = Object.assign(formData.value, data || {});
+  function handleOpen(data) {
+    formData.value = { ...(data || {}) };
     editRef.value?.handleOpen(formData.value);
   }
 

+ 16 - 15
src/views/customer/blackMobile/index.vue

@@ -13,12 +13,17 @@
             plain
             v-permission="'customer:blackMobile:add'"
             @click="handleUpdate()"
+            :icon="PlusOutlined"
           >
             新增
           </el-button>
 
           <div class="flex items-center">
-            <el-input v-model="queryParams.mobile" class="mr-6" placeholder="请输入手机号"></el-input>
+            <el-input
+              v-model="queryParams.mobile"
+              class="mr-6"
+              placeholder="请输入手机号"
+            ></el-input>
             <el-button
               style="width: 80px"
               type="primary"
@@ -62,33 +67,29 @@
 
 <script setup>
   import { ref, reactive } from 'vue';
+  import { PlusOutlined } from '@/components/icons';
   import CommonTable from '@/components/CommonPage/CommonTable.vue';
   import pageEdit from './components/page-edit.vue';
+  import request from '@/utils/request';
 
   defineOptions({ name: 'blackMobileList' });
 
   const queryParams = reactive({ mobile: '' });
   /** 表格列配置 */
   const columns = ref([
-    { label: '拉黑号码', prop: 'phoneNum', align: 'center' },
-    { label: '拉黑时间', prop: 'createTime', align: 'center', width: 200 },
-    { label: '操作员', prop: 'contactsName', align: 'center' },
-    { label: '拉黑原因', prop: 'addressDetail', align: 'center', minWidth: 160 },
-    {
-      columnKey: 'action',
-      label: '操作',
-      width: 220,
-      align: 'center',
-      slot: 'action'
-    }
+    { label: '拉黑号码', prop: 'mobile', align: 'center' },
+    { label: '拉黑时间', prop: 'createTime', align: 'center' },
+    { label: '操作员', prop: 'createName', align: 'center' },
+    { label: '拉黑原因', prop: 'reason', align: 'center', minWidth: 160 },
+    { columnKey: 'action', label: '操作', align: 'center', slot: 'action' }
   ]);
 
   /** 页面组件实例 */
   const pageRef = ref(null);
 
   const pageConfig = reactive({
-    pageUrl: '/baseinfo/godown/pagelist',
-    exportUrl: '/baseinfo/godown/export',
+    pageUrl: '/user/mobileBlack/pagelist',
+    exportUrl: '/user/mobileBlack/export',
     fileName: '黑名单手机号',
     cacheKey: 'blackMobileTable'
   });
@@ -102,7 +103,7 @@
   function handleDelete(row) {
     pageRef.value?.messageBoxConfirm({
       message: '确认删除?',
-      fetch: () => request.post('/baseinfo/godown/delete', { id: row.id })
+      fetch: () => request.post('/user/mobileBlack/delete', [row.id])
     });
   }
 

+ 2 - 2
src/views/customer/blacklist/components/page-search.vue

@@ -18,8 +18,8 @@
 
   const formItems = computed(() => {
     return [
-      { type: 'input', label: '客户昵称/uid', prop: 'name' },
-      { type: 'input', label: '联系方式', prop: 'phone' },
+      { type: 'input', label: '客户昵称/客户名', prop: 'nickName' },
+      { type: 'input', label: '联系方式', prop: 'mobile' },
     ];
   });
 

+ 47 - 91
src/views/customer/blacklist/index.vue

@@ -2,16 +2,13 @@
   <ele-page flex-table>
     <page-search @search="reload"></page-search>
 
-    <common-table ref="pageRef" :pageConfig="pageConfig" :columns="columns">
-      <template #toolbar>
-        <el-button
-          type="primary"
-          v-permission="'customer:blacklist:add'"
-          @click="handleUpdate()"
-          :icon="PlusOutlined"
-        >
-          添加黑名单
-        </el-button>
+    <common-table
+      ref="pageRef"
+      :pageConfig="pageConfig"
+      :columns="columns"
+      :tools="false"
+    >
+      <!-- 
         <el-button
           type="success"
           plain
@@ -21,19 +18,19 @@
         >
           导出EXCEL
         </el-button>
-      </template>
-
-      <template #picture="{ row }">
-        <div class="flex flex-col">
-          <el-avatar
-            src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"
-          />
-          <el-text style="text-align: center">{{ row.schoolName }}</el-text>
+      </template> -->
+      <template #nickName="{ row }">
+        <div class="flex flex-col items-center">
+          <el-avatar :src="row.imgPath" />
+          <ele-text class="mt-1">{{ row.nickName }}</ele-text>
         </div>
       </template>
-
-      <template #schoolTag="{ row }">
-        <dict-data code="school_tag" type="tag" :model-value="row.schoolTag" />
+      <template #status="{ row }">
+        <dict-data
+          code="user_info_status"
+          type="tag"
+          :model-value="row.status"
+        />
       </template>
 
       <template #action="{ row }">
@@ -50,7 +47,7 @@
             type="warning"
             link
             v-permission="'customer:blacklist:userTag'"
-            @click="handleUpdate(row)"
+            @click="handleBindTag(row)"
           >
             [用户标签]
           </el-button>
@@ -58,7 +55,7 @@
             type="warning"
             link
             v-permission="'customer:blacklist:accountDetail'"
-            @click="handleUpdate(row)"
+            @click="handleDetail(row, 'accountChange')"
           >
             [账户明细]
           </el-button>
@@ -74,7 +71,7 @@
             type="danger"
             link
             v-permission="'customer:blacklist:remove'"
-            @click="handleAddBlacklist(row)"
+            @click="handleRemoveBlacklist(row)"
           >
             [移出黑名单]
           </el-button>
@@ -85,74 +82,33 @@
     <page-edit ref="editRef" @success="reload()"></page-edit>
     <blacklist-history ref="historyRef"></blacklist-history>
     <customer-detail ref="detailRef"></customer-detail>
+    <user-bind-tag ref="tagRef"></user-bind-tag>
   </ele-page>
 </template>
 
 <script setup>
   import { ref, reactive } from 'vue';
-  import { ElMessageBox } from 'element-plus/es';
-  import { EleMessage } from 'ele-admin-plus/es';
   import { DownloadOutlined, PlusOutlined } from '@/components/icons';
   import CommonTable from '@/components/CommonPage/CommonTable.vue';
   import pageSearch from './components/page-search.vue';
-  import pageEdit from './components/page-edit.vue'
+  import pageEdit from './components/page-edit.vue';
   import blacklistHistory from '@/views/customer/list/components/blacklist-history.vue';
   import customerDetail from '@/views/customer/list/components/customer-detail.vue';
-  import { useDictData } from '@/utils/use-dict-data';
+  import userBindTag from '@/views/recycleOrder/components/user-bind-tag.vue';
   import request from '@/utils/request';
 
-  defineOptions({ name: 'customerList' });
-  const [schoolLevelDicts, schoolTagDicts] = useDictData([
-    'school_level',
-    'school_tag'
-  ]);
+  defineOptions({ name: 'blackList' });
 
   /** 表格列配置 */
   const columns = ref([
-    {
-      type: 'selection',
-      columnKey: 'selection',
-      width: 50,
-      align: 'center',
-      fixed: 'left'
-    },
-    {
-      label: '用户昵称',
-      prop: 'schoolName',
-      align: 'center',
-      minWidth: 100,
-      slot: 'picture'
-    },
-    { label: '用户名', prop: 'provinceName', align: 'center' },
-    { label: '用户OpenId', prop: 'cityName', align: 'center' },
-    { label: '手机号', prop: 'departmentName', align: 'center' },
-    {
-      label: '用户类型',
-      prop: 'schoolLevel',
-      align: 'center',
-      formatter: (row) =>
-        schoolLevelDicts.value.find((d) => d.dictValue == row.schoolLevel)
-          ?.dictLabel
-    },
-    { label: '用户标签', prop: 'departmentName', align: 'center' },
-    {
-      label: '状态',
-      prop: 'schoolTag',
-      align: 'center',
-      slot: 'schoolTag',
-      formatter: (row) =>
-        schoolTagDicts.value.find((d) => d.dictValue == row.schoolTag)
-          ?.dictLabel
-    },
-    {
-      label: '创建时间',
-      prop: 'createTime',
-      align: 'center',
-      width: 160,
-      formatter: (row) => {
-        return row.createTime ? new Date(row.createTime).toLocaleString() : '';
-      }
-    },
+    { label: '用户昵称', prop: 'nickName', align: 'center', slot: 'nickName' },
+    { label: '用户名', prop: 'id', align: 'center' },
+    { label: '用户OpenId', prop: 'openId', align: 'center' },
+    { label: '手机号', prop: 'mobile', align: 'center' },
+    { label: '用户类型', prop: 'platName', align: 'center' },
+    { label: '用户标签', prop: 'userTagNames', align: 'center' },
+    { label: '状态', prop: 'statusName', align: 'center', slot: 'status' },
+    { label: '创建时间', prop: 'createTime', align: 'center', width: 160 },
     {
       columnKey: 'action',
       label: '操作',
@@ -166,10 +122,10 @@
   const pageRef = ref(null);
 
   const pageConfig = reactive({
-    pageUrl: '/baseinfo/schoolInfo/list',
-    exportUrl: '/baseinfo/schoolInfo/export',
-    fileName: '客户列表',
-    cacheKey: 'customerListTable'
+    pageUrl: '/user/black/pagelist',
+    exportUrl: '/user/black/export',
+    fileName: '黑名单列表',
+    cacheKey: 'blackListTable'
   });
 
   //刷新表格
@@ -191,26 +147,21 @@
   }
   //导出excel
   function handleExportExcel() {
-    pageRef.value?.exportData('高校列表');
+    pageRef.value?.exportData('黑名单列表');
   }
 
   //移出黑名单
-  function handleAddBlacklist(row) {
+  function handleRemoveBlacklist(row) {
     pageRef.value?.messageBoxConfirm({
       message: '确认移出黑名单?',
-      fetch: () => request.post('/baseinfo/schoolInfo/edit', data)
+      fetch: () => request.post('/user/black/delete', { userId: row.id })
     });
   }
 
-  //编辑页面
-  const editRef = ref(null);
-  function handleUpdate(row) {
-    editRef.value?.handleOpen(row);
-  }
   //详情页面
   const detailRef = ref(null);
-  function handleDetail(row) {
-    detailRef.value?.handleOpen(row);
+  function handleDetail(row, type) {
+    detailRef.value?.handleOpen(row, type);
   }
 
   //拉黑历史
@@ -218,4 +169,9 @@
   function handleBlacklistHistory(row) {
     historyRef.value?.handleOpen(row);
   }
+  //用户标签
+  const tagRef = ref(null);
+  function handleBindTag(row) {
+    tagRef.value?.handleOpen(row);
+  }
 </script>

+ 14 - 18
src/views/customer/list/components/blacklist-history.vue

@@ -1,14 +1,13 @@
 <!-- 编辑弹窗 -->
 <template>
   <ele-modal form :width="800" v-model="visible" title="拉黑历史">
-    <SimpleTable
+    <ele-data-table
       ref="tableRef"
-      style="width: 100%"
+      class="flex-1"
+      row-key="id"
       :columns="columns"
-      :fetchPage="fetchPage"
-      :formaterData="formaterData"
-    >
-    </SimpleTable>
+      :data="dataList"
+    ></ele-data-table>
 
     <template #footer>
       <el-button @click="handleCancel">关闭</el-button>
@@ -18,8 +17,6 @@
 
 <script setup>
   import { ref, reactive, nextTick } from 'vue';
-  import { Flag, ChatDotSquare } from '@element-plus/icons-vue';
-  import SimpleTable from '@/components/CommonPage/SimpleTable.vue';
   import request from '@/utils/request';
 
   /** 弹窗是否打开 */
@@ -31,30 +28,29 @@
   };
 
   /** 弹窗打开事件 */
-  let bookId = ref();
+  let dataList = ref([]);
   const tableRef = ref();
   const handleOpen = (row) => {
     visible.value = true;
-    bookId.value = row.id;
     nextTick(() => {
-      tableRef.value?.reload();
+      fetchPage(row.id);
     });
   };
 
-  function formaterData(data) {
-    return { rows: data.data };
-  }
+  function fetchPage(userId) {
+    request.get('/user/black/getUserBlackReocrd/' + userId).then((res) => {
+      dataList.value = res.data;
 
-  function fetchPage(params) {
-    return request.get('/book/bookInfo/getChangeLog/' + bookId.value, {
-      params
+      if (res.data.code == 200) {
+        dataList.value = res.data.data;
+      }
     });
   }
 
   // 表格数据
   const columns = reactive([
     { label: '拉黑时间', prop: 'createTime', width: 180 },
-    { label: '拉黑原因', prop: 'changeAttribute' },
+    { label: '拉黑原因', prop: 'reason' },
     { label: '操作员', prop: 'createName', width: 100 }
   ]);
 

+ 23 - 10
src/views/customer/list/components/customer-detail.vue

@@ -11,11 +11,8 @@
     }"
   >
     <div class="flex items-center">
-      <el-avatar
-        :size="50"
-        src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"
-      />
-      <el-text style="margin-left: 10px">微信用户</el-text>
+      <el-avatar :size="50" :src="detail.imgPath" />
+      <el-text style="margin-left: 10px">{{ detail.nickName }}</el-text>
     </div>
 
     <div class="flex items-center mb-5 mt-5 ml-3">
@@ -33,10 +30,10 @@
 
     <el-tabs v-model="activeName" class="demo-tabs flex-1">
       <el-tab-pane label="用户基础信息" name="baseinfo">
-        <base-info />
+        <base-info :detail="detail" />
       </el-tab-pane>
       <el-tab-pane label="余额变动" name="accountChange">
-        <account-change />
+        <account-change :row="currentRow" />
       </el-tab-pane>
       <el-tab-pane label="扫描记录" name="scanLog">
         <scan-log />
@@ -57,14 +54,30 @@
   import scanLog from '@/views/customer/list/components/detail/scan-log.vue';
   import recommendInfo from '@/views/customer/list/components/detail/recommend-info.vue';
   import fallbackInfo from '@/views/customer/list/components/detail/fallback-info.vue';
+  import request from '@/utils/request';
   const visible = ref(false);
+  const activeName = ref('baseinfo');
 
-  function handleOpen(row) {
+  const currentRow = ref();
+  function handleOpen(row, tabName) {
     visible.value = true;
-    nextTick(() => {});
+    currentRow.value = row;
+    nextTick(() => {
+      activeName.value = tabName || 'baseinfo';
+      getUserInfo(row.id);
+    });
   }
 
-  const activeName = ref('baseinfo');
+  //获取用户信息
+  const detail = ref({});
+  const getUserInfo = (id) => {
+    request({
+      url: `/user/userInfo/getInfo/${id}`,
+      method: 'get'
+    }).then((res) => {
+      detail.value = res.data.data;
+    });
+  };
 
   defineExpose({
     handleOpen

+ 31 - 11
src/views/customer/list/components/detail/account-change.vue

@@ -15,22 +15,42 @@
 
   defineOptions({ name: 'accountChangeLog' });
 
-  /** 表格列配置 */
-  const columns = ref([
-    { label: '变动记录', prop: 'cover', align: 'center' },
-    { label: '余额变化', prop: 'baseInfo', align: 'center' },
-    { label: '变化后余额', prop: 'money', align: 'center' },
-    { label: '发生时间', prop: 'createTime', align: 'center' }
-  ]);
-
-  /** 页面组件实例 */
-  const pageRef = ref(null);
+  const props = defineProps({
+    row: {
+      type: Object,
+      default() {
+        return {};
+      }
+    }
+  });
 
   const pageConfig = reactive({
-    pageUrl: '/book/bookInfo/list',
+    pageUrl: '',
     fileName: '余额变动',
     cacheKey: 'accountChangeLogTable'
   });
+  /** 页面组件实例 */
+  const pageRef = ref(null);
+
+  watch(
+    () => props.row,
+    (row) => {
+      pageConfig.pageUrl = `/user/userInfo/getUserAccountChangeLog/${row.id}`;
+      pageRef.value?.reload();
+    },
+    {
+      immediate: true,
+      deep: true
+    }
+  );
+
+  /** 表格列配置 */
+  const columns = ref([
+    { label: '变动记录', prop: 'changeTypeName', align: 'center' },
+    { label: '余额变化', prop: 'changeMoney', align: 'center' },
+    { label: '变化后余额', prop: 'afterMoney', align: 'center' },
+    { label: '发生时间', prop: 'createTime', align: 'center' }
+  ]);
 
   //刷新表格
   function reload(where) {

+ 17 - 9
src/views/customer/list/components/detail/base-info.vue

@@ -2,22 +2,30 @@
   <div class="base-info">
     <div class="common-title">基础信息</div>
     <el-row :gutter="12" class="p-4 leading-8">
-      <el-col :span="8">用户名:048a4d98sad8a7d9</el-col>
-      <el-col :span="8">性别:男</el-col>
-      <el-col :span="8">真实姓名:***</el-col>
-      <el-col :span="8">联系方式:19299299222</el-col>
-      <el-col :span="16">地址信息:河南鹤壁浚县*********</el-col>
+      <el-col :span="8">用户名:{{ detail.id }}</el-col>
+      <el-col :span="8">性别:{{ detail.sexName || '-' }}</el-col>
+      <el-col :span="8">真实姓名:{{ detail.trueName || '-' }}</el-col>
+      <el-col :span="8">联系方式:{{ detail.mobile || '-' }}</el-col>
     </el-row>
     <div class="common-title">概况信息</div>
     <el-row :gutter="12" class="p-4 leading-8">
-      <el-col :span="24">用户标签:多次购买</el-col>
-      <el-col :span="12">注册时间:2024-06-26 21:55:03</el-col>
-      <el-col :span="12">最近登录时间:2024-06-26 21:55:03</el-col>
+      <el-col :span="24">用户标签:{{ detail.userTagNames || '-' }}</el-col>
+      <el-col :span="12">注册时间:{{ detail.createTime || '-' }}</el-col>
+      <el-col :span="12">最近登录时间:{{ detail.loginTime || '-' }}</el-col>
     </el-row>
 
     <div class="common-title">备注信息</div>
     <el-row :gutter="12" class="p-4 leading-8">
-      <el-col :span="24">备注:无</el-col>
+      <el-col :span="24">备注:{{ detail.remark || '' }}</el-col>
     </el-row>
   </div>
 </template>
+
+<script setup>
+  const props = defineProps({
+    detail: {
+      type: Object,
+      default: () => ({})
+    }
+  });
+</script>

+ 4 - 4
src/views/customer/list/components/page-search.vue

@@ -18,16 +18,16 @@
 
   const formItems = computed(() => {
     return [
-      { type: 'input', label: '用户名', prop: 'name' },
+      { type: 'input', label: '用户名', prop: 'id' },
       { type: 'input', label: '用户OpenId', prop: 'openid' },
       { type: 'input', label: '用户昵称', prop: 'nickName' },
-      { type: 'input', label: '手机号', prop: 'phone' },
+      { type: 'input', label: '手机号', prop: 'mobile' },
       {
         type: 'dictSelect',
         label: '状态',
-        prop: 'schoolLevel',
+        prop: 'status',
         props: {
-          code: 'school_level'
+          code: 'user_info_status'
         }
       }
     ];

+ 71 - 67
src/views/customer/list/index.vue

@@ -15,17 +15,18 @@
         </el-button>
       </template>
 
-      <template #picture="{ row }">
-        <div class="flex flex-col">
-          <el-avatar
-            src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"
-          />
-          <el-text style="text-align: center">{{ row.schoolName }}</el-text>
+      <template #nickName="{ row }">
+        <div class="flex flex-col items-center">
+          <el-avatar :src="row.imgPath" />
+          <ele-text class="mt-1">{{ row.nickName }}</ele-text>
         </div>
       </template>
-
-      <template #schoolTag="{ row }">
-        <dict-data code="school_tag" type="tag" :model-value="row.schoolTag" />
+      <template #status="{ row }">
+        <dict-data
+          code="user_info_status"
+          type="tag"
+          :model-value="row.status"
+        />
       </template>
 
       <template #action="{ row }">
@@ -42,7 +43,7 @@
             type="warning"
             link
             v-permission="'customer:list:userTag'"
-            @click="handleUpdate(row)"
+            @click="handleBindTag(row)"
           >
             [用户标签]
           </el-button>
@@ -50,7 +51,7 @@
             type="warning"
             link
             v-permission="'customer:list:accountDetail'"
-            @click="handleUpdate(row)"
+            @click="handleDetail(row, 'accountChange')"
           >
             [账户明细]
           </el-button>
@@ -63,6 +64,7 @@
             [拉黑历史]
           </el-button>
           <el-button
+            v-if="row.status == 1"
             type="danger"
             link
             v-permission="'customer:list:blacklist'"
@@ -70,12 +72,22 @@
           >
             [加入黑名单]
           </el-button>
+          <el-button
+            v-else
+            type="danger"
+            link
+            v-permission="'customer:list:blacklist'"
+            @click="handleRemoveBlacklist(row)"
+          >
+            [移除黑名单]
+          </el-button>
         </div>
       </template>
     </common-table>
 
     <blacklist-history ref="historyRef"></blacklist-history>
     <customer-detail ref="detailRef"></customer-detail>
+    <user-bind-tag ref="tagRef"></user-bind-tag>
   </ele-page>
 </template>
 
@@ -88,61 +100,21 @@
   import pageSearch from './components/page-search.vue';
   import blacklistHistory from '@/views/customer/list/components/blacklist-history.vue';
   import customerDetail from '@/views/customer/list/components/customer-detail.vue';
-  import { useDictData } from '@/utils/use-dict-data';
+  import userBindTag from '@/views/recycleOrder/components/user-bind-tag.vue';
   import request from '@/utils/request';
 
   defineOptions({ name: 'customerList' });
-  const [schoolLevelDicts, schoolTagDicts] = useDictData([
-    'school_level',
-    'school_tag'
-  ]);
 
   /** 表格列配置 */
   const columns = ref([
-    {
-      type: 'selection',
-      columnKey: 'selection',
-      width: 50,
-      align: 'center',
-      fixed: 'left'
-    },
-    {
-      label: '用户昵称',
-      prop: 'schoolName',
-      align: 'center',
-      minWidth: 100,
-      slot: 'picture'
-    },
-    { label: '用户名', prop: 'provinceName', align: 'center' },
-    { label: '用户OpenId', prop: 'cityName', align: 'center' },
-    { label: '手机号', prop: 'departmentName', align: 'center' },
-    {
-      label: '用户类型',
-      prop: 'schoolLevel',
-      align: 'center',
-      formatter: (row) =>
-        schoolLevelDicts.value.find((d) => d.dictValue == row.schoolLevel)
-          ?.dictLabel
-    },
-    { label: '用户标签', prop: 'departmentName', align: 'center' },
-    {
-      label: '状态',
-      prop: 'schoolTag',
-      align: 'center',
-      slot: 'schoolTag',
-      formatter: (row) =>
-        schoolTagDicts.value.find((d) => d.dictValue == row.schoolTag)
-          ?.dictLabel
-    },
-    {
-      label: '创建时间',
-      prop: 'createTime',
-      align: 'center',
-      width: 160,
-      formatter: (row) => {
-        return row.createTime ? new Date(row.createTime).toLocaleString() : '';
-      }
-    },
+    { label: '用户昵称', prop: 'nickName', align: 'center', slot: 'nickName' },
+    { label: '用户名', prop: 'id', align: 'center' },
+    { label: '用户OpenId', prop: 'openId', align: 'center' },
+    { label: '手机号', prop: 'mobile', align: 'center' },
+    { label: '用户类型', prop: 'platName', align: 'center' },
+    { label: '用户标签', prop: 'userTagNames', align: 'center' },
+    { label: '状态', prop: 'statusName', align: 'center', slot: 'status' },
+    { label: '创建时间', prop: 'createTime', align: 'center', width: 160 },
     {
       columnKey: 'action',
       label: '操作',
@@ -156,8 +128,8 @@
   const pageRef = ref(null);
 
   const pageConfig = reactive({
-    pageUrl: '/baseinfo/schoolInfo/list',
-    exportUrl: '/baseinfo/schoolInfo/export',
+    pageUrl: '/user/userInfo/pagelist',
+    exportUrl: '/user/userInfo/export',
     fileName: '客户列表',
     cacheKey: 'customerListTable'
   });
@@ -181,14 +153,40 @@
   }
   //导出excel
   function handleExportExcel() {
-    pageRef.value?.exportData('高校列表');
+    pageRef.value?.exportData('用户列表');
   }
 
   //加入黑名单
   function handleAddBlacklist(row) {
+    ElMessageBox.confirm('确认加入黑名单?', '提示', {
+      confirmButtonText: '确定',
+      canceledButtonText: '关闭',
+      showInput: true,
+      inputPlaceholder: '请输入拉黑原因'
+    }).then(({ value }) => {
+      request
+        .post('/user/black/add', { userId: row.id, reason: value })
+        .then((res) => {
+          if (res.data.code == 200) {
+            ElMessage({
+              type: 'success',
+              message: '操作成功'
+            });
+            reload();
+          } else {
+            ElMessage({
+              type: 'error',
+              message: res.data.msg
+            });
+          }
+        });
+    });
+  }
+  //移除黑名单
+  function handleRemoveBlacklist(row) {
     pageRef.value?.messageBoxConfirm({
-      message: '确认加入黑名单?',
-      fetch: () => request.post('/baseinfo/schoolInfo/edit', data)
+      message: '确认移出黑名单?',
+      fetch: () => request.post('/user/black/delete', { userId: row.id })
     });
   }
 
@@ -199,8 +197,8 @@
   }
   //详情页面
   const detailRef = ref(null);
-  function handleDetail(row) {
-    detailRef.value?.handleOpen(row);
+  function handleDetail(row, type) {
+    detailRef.value?.handleOpen(row, type);
   }
 
   //拉黑历史
@@ -208,4 +206,10 @@
   function handleBlacklistHistory(row) {
     historyRef.value?.handleOpen(row);
   }
+
+  //用户标签
+  const tagRef = ref(null);
+  function handleBindTag(row) {
+    tagRef.value?.handleOpen(row);
+  }
 </script>

+ 42 - 0
src/views/customer/userTag/components/page-edit.vue

@@ -0,0 +1,42 @@
+<!-- 搜索表单 -->
+<template>
+  <simple-form-modal
+    :title="title"
+    width="420"
+    :items="formItems"
+    ref="editRef"
+    :baseUrl="baseUrl"
+    @success="(data) => emit('success', data)"
+  ></simple-form-modal>
+</template>
+
+<script setup>
+  import { reactive, ref, defineEmits, getCurrentInstance } from 'vue';
+  import { useFormData } from '@/utils/use-form-data';
+  import SimpleFormModal from '@/components/CommonPage/SimpleFormModal.vue';
+  const { proxy } = getCurrentInstance();
+
+  const emit = defineEmits(['success']);
+
+  const formItems = computed(() => {
+    return [
+      { type: 'input', label: '标签名称', prop: 'tagName', required: true }
+    ];
+  });
+  //默认值
+  const baseUrl = reactive({
+    add: '/user/tag/add',
+    update: '/user/tag/update'
+  });
+  const formData = ref({});
+
+  const editRef = ref(null);
+  const title = ref('添加标签');
+  function handleOpen(data) {
+    title.value = data && data.id ? '编辑标签' : '添加标签';
+    formData.value = { ...(data || {}) };
+    editRef.value?.handleOpen(formData.value);
+  }
+
+  defineExpose({ handleOpen });
+</script>

+ 114 - 0
src/views/customer/userTag/index.vue

@@ -0,0 +1,114 @@
+<template>
+  <ele-page flex-table>
+    <common-table
+      ref="pageRef"
+      :pageConfig="pageConfig"
+      :columns="columns"
+      :tools="false"
+    >
+      <template #toolbar>
+        <div class="flex justify-between items-center">
+          <el-button
+            type="primary"
+            plain
+            v-permission="'customer:blackMobile:add'"
+            @click="handleUpdate()"
+            :icon="PlusOutlined"
+          >
+            新增标签
+          </el-button>
+
+          <div class="flex items-center">
+            <el-input
+              v-model="queryParams.tagNameLike"
+              class="mr-6"
+              placeholder="请输入标签名称"
+            ></el-input>
+            <el-button
+              style="width: 80px"
+              type="primary"
+              @click="reload(queryParams)"
+              >查询</el-button
+            >
+            <el-button
+              style="width: 80px"
+              type="info"
+              @click="queryParams.tagNameLike = ''"
+              >重置</el-button
+            >
+          </div>
+        </div>
+      </template>
+
+      <template #action="{ row }">
+        <div>
+          <el-button
+            type="primary"
+            link
+            v-permission="'customer:blackMobile:detail'"
+            @click="handleUpdate(row)"
+          >
+            [编辑]
+          </el-button>
+          <el-button
+            type="danger"
+            link
+            v-permission="'customer:blackMobile:delete'"
+            @click="handleDelete(row)"
+          >
+            [删除]
+          </el-button>
+        </div>
+      </template>
+    </common-table>
+    <page-edit ref="pageEditRef" @success="reload()" />
+  </ele-page>
+</template>
+
+<script setup>
+  import { ref, reactive } from 'vue';
+  import { PlusOutlined } from '@/components/icons';
+  import CommonTable from '@/components/CommonPage/CommonTable.vue';
+  import pageEdit from './components/page-edit.vue';
+  import request from '@/utils/request';
+
+  defineOptions({ name: 'blackMobileList' });
+
+  const queryParams = reactive({ tagNameLike: '' });
+  /** 表格列配置 */
+  const columns = ref([
+    { label: '序号', type: 'index', align: 'center', width: 100 },
+    { label: '标签名称', prop: 'tagName', align: 'center' },
+    { label: '标签用户数', prop: 'useNum', align: 'center' },
+    { columnKey: 'action', label: '操作', align: 'center', slot: 'action' }
+  ]);
+
+  /** 页面组件实例 */
+  const pageRef = ref(null);
+
+  const pageConfig = reactive({
+    pageUrl: '/user/tag/pagelist',
+    exportUrl: '/user/tag/export',
+    fileName: '用户标签列表',
+    cacheKey: 'blackMobileTable'
+  });
+
+  //刷新表格
+  function reload(where) {
+    pageRef.value?.reload(where);
+  }
+
+  //删除
+  function handleDelete(row) {
+    pageRef.value?.messageBoxConfirm({
+      message: '确认删除?',
+      fetch: () => request.post('/user/tag/delete', [row.id])
+    });
+  }
+
+  //编辑
+  const pageEditRef = ref(null);
+  function handleUpdate(row) {
+    pageEditRef.value?.handleOpen(row);
+  }
+</script>

+ 434 - 0
src/views/recycleOrder/components/order-page-all.vue

@@ -0,0 +1,434 @@
+<template>
+  <ele-page flex-table>
+    <!-- 搜索表单 -->
+    <order-search @search="reload" />
+    <ele-card :body-style="{ paddingTop: '8px' }" flex-table>
+      <!-- 表格 -->
+      <ele-pro-table
+        ref="tableRef"
+        row-key="postId"
+        :columns="columns"
+        :datasource="datasource"
+        :show-overflow-tooltip="true"
+        v-model:selections="selections"
+        highlight-current-row
+        :export-config="{ fileName: pageConfig.fileName }"
+        :cache-key="pageConfig.cacheKey"
+      >
+        <template #toolbar>
+          <slot name="toolbar"></slot>
+        </template>
+
+        <template #status="{ row }">
+          <dict-data
+            code="sys_normal_disable"
+            type="tag"
+            :model-value="row.status"
+          />
+        </template>
+        <template #orderNumber="{ row }">
+          <order-number :row="row"></order-number>
+        </template>
+        <template #customer="{ row }">
+          <order-customer :row="row"></order-customer>
+        </template>
+        <template #amount="{ row }">
+          <order-amount :row="row"></order-amount>
+        </template>
+        <template #time="{ row }">
+          <order-time :row="row"></order-time>
+        </template>
+        <template #remarks="{ row }">
+          <el-popover trigger="hover" width="240px">
+            <template #reference>
+              <el-button
+                :icon="Flag"
+                link
+                style="font-size: 20px"
+                @click="handleRemarks(row)"
+              >
+              </el-button>
+            </template>
+            <orderTimeline
+              :records="activities"
+              title="备注历史记录"
+            ></orderTimeline>
+          </el-popover>
+        </template>
+
+        <template #action="{ row }">
+          <div>
+            <el-button
+              type="success"
+              link
+              v-permission="usePermission('detail')"
+              @click="toOrderDetail(row)"
+            >
+              [订单详情]
+            </el-button>
+            <el-button
+              type="warning"
+              link
+              v-permission="usePermission('log')"
+              @click="openOrderLog(row)"
+            >
+              [订单日志]
+            </el-button>
+            <el-button
+              type="danger"
+              link
+              v-if="row.status == 2"
+              v-permission="usePermission('audit')"
+              @click="cancelOrder(row)"
+            >
+              [初步审核]
+            </el-button>
+            <!-- 3 已初审-带上门取件 5已揽件-待签收 6已签收-待确认到货 -->
+            <el-button
+              type="danger"
+              link
+              v-if="[3, 5, 6].includes(row.status)"
+              v-permission="usePermission('cancel')"
+              @click="cancelOrder(row)"
+            >
+              [取消订单]
+            </el-button>
+            <!-- 3 已初审-带上门取件 5已揽件-待签收 6已签收-待确认到货 8待回收审核 9审核未提交 10待付款 -->
+            <el-button
+              type="primary"
+              link
+              v-if="[3, 5, 6, 8, 9, 10].includes(row.status)"
+              v-permission="usePermission('fallback')"
+              @click="fallbackOrder(row)"
+            >
+              [回退状态]
+            </el-button>
+            <el-button
+              type="success"
+              link
+              v-if="row.status == 3"
+              v-permission="usePermission('materialPickup')"
+              @click="materialPickup(row)"
+            >
+              [物流揽件]
+            </el-button>
+            <el-button
+              type="success"
+              link
+              v-if="row.status == 5"
+              v-permission="usePermission('receive')"
+              @click="materialPickup(row)"
+            >
+              [物流签收]
+            </el-button>
+            <el-button
+              type="success"
+              link
+              v-if="row.status == 6"
+              v-permission="usePermission('confirmReceipt')"
+              @click="materialPickup(row)"
+            >
+              [确认收货]
+            </el-button>
+            <el-button
+              type="success"
+              link
+              v-if="[8, 9].includes(row.status)"
+              v-permission="usePermission('arrivalAudit')"
+              @click="handleArrivalAudit(row)"
+            >
+              [到货审核]
+            </el-button>
+            <el-button
+              type="danger"
+              link
+              v-if="[8, 9, 10, 11].includes(row.status)"
+              v-permission="usePermission('auditScreenshot')"
+              @click="handleAuditScreenshot(row)"
+            >
+              [审核截图]
+            </el-button>
+            <el-button
+              type="danger"
+              link
+              v-if="row.status == 10"
+              v-permission="usePermission('payment')"
+              @click="handleBatchPayment(row)"
+            >
+              [支付书款]
+            </el-button>
+            <el-button
+              type="danger"
+              link
+              v-if="row.status == 11"
+              v-permission="usePermission('afterSalesPayment')"
+              @click="handleAfterSalesPayment(row)"
+            >
+              [售后补款]
+            </el-button>
+            <el-button
+              type="warning"
+              link
+              v-permission="usePermission('userTag')"
+              @click="openEditUserTag(row)"
+            >
+              [用户标签]
+            </el-button>
+            <el-button
+              type="success"
+              link
+              v-if="row.status == 2"
+              v-permission="usePermission('modifyAddress')"
+              @click="cancelOrder(row)"
+            >
+              [修改地址]
+            </el-button>
+            <el-button
+              type="success"
+              link
+              v-if="[8, 9, 10, 11].includes(row.status)"
+              v-permission="usePermission('sendSMS')"
+              @click="handleSendSMS(row)"
+            >
+              [推送短信]
+            </el-button>
+            <el-button
+              type="info"
+              link
+              v-if="[8, 9, 10].includes(row.status)"
+              v-permission="usePermission('smsLog')"
+              @click="handleSmsLog(row)"
+            >
+              [短信记录]
+            </el-button>
+            <el-button
+              color="#7728f5"
+              link
+              plain
+              v-if="row.status == 11"
+              v-permission="usePermission('applyForOrderClaim')"
+              @click="applyForOrderClaim(row)"
+            >
+              [申请订单理赔]
+            </el-button>
+            <el-button
+              color="#7728f5"
+              link
+              plain
+              v-if="[7].includes(row.status)"
+              v-permission="usePermission('applyForOrderRestore')"
+              @click="handleApplyForOrderRestore(row)"
+            >
+              [申请恢复订单]
+            </el-button>
+            <el-button
+              type="danger"
+              link
+              v-if="[5, 6].includes(row.status)"
+              v-permission="usePermission('interception')"
+              @click="applyForInterception(row)"
+            >
+              [申请拦截退回]
+            </el-button>
+          </div>
+        </template>
+      </ele-pro-table>
+    </ele-card>
+
+    <slot></slot>
+    <orderRemarks ref="remarksRef" />
+  </ele-page>
+</template>
+
+<script setup>
+  import { ref, getCurrentInstance } from 'vue';
+  import { ElMessageBox } from 'element-plus/es';
+  import { EleMessage } from 'ele-admin-plus/es';
+  import { DownloadOutlined } from '@/components/icons';
+  import { Flag, ChatDotSquare } from '@element-plus/icons-vue';
+  import OrderSearch from '../components/order-search.vue';
+  import OrderNumber from '@/views/recycleOrder/components/order-number.vue';
+  import OrderCustomer from '@/views/recycleOrder/components/order-customer.vue';
+  import OrderAmount from '@/views/recycleOrder/components/order-amount.vue';
+  import OrderTime from '@/views/recycleOrder/components/order-time.vue';
+  import { useDictData } from '@/utils/use-dict-data';
+  import { download, toFormData, checkDownloadRes } from '@/utils/common';
+  import orderRemarks from '@/views/recycleOrder/components/order-remarks.vue';
+  import orderTimeline from '@/views/recycleOrder/components/order-timeline.vue';
+
+  let props = defineProps({
+    pageConfig: {
+      type: Object,
+      default: () => ({
+        cacheKey: 'recycleOrderTable',
+        fileName: '回收订单查询'
+      })
+    },
+    pageUrl: { type: String, default: '/system/post/list' },
+    exportUrl: { type: String, default: '/system/post/export' },
+    permissionKey: { type: String, default: 'search' }
+  });
+  const usePermission = computed(() => (opts) => {
+    return `recycleOrder:${props.permissionKey}:${opts}`;
+  });
+
+  let { proxy } = getCurrentInstance();
+  /** 字典数据 */
+  const [statusDicts] = useDictData(['sys_normal_disable']);
+
+  /** 表格实例 */
+  const tableRef = ref(null);
+
+  /** 表格列配置 */
+  const columns = ref([
+    {
+      type: 'selection',
+      columnKey: 'selection',
+      width: 50,
+      align: 'center',
+      fixed: 'left'
+    },
+    { label: '单号', prop: 'orderNumber', slot: 'orderNumber', minWidth: 180 },
+    { label: '客户', prop: 'customer', slot: 'customer', minWidth: 360 },
+    { label: '金额', prop: 'amount', slot: 'amount', minWidth: 160 },
+    {
+      label: '状态',
+      prop: 'status',
+      slot: 'status',
+      formatter: (row) =>
+        statusDicts.value.find((d) => d.dictValue == row.status)?.dictLabel
+    },
+    { label: '时间', prop: 'time', slot: 'time', minWidth: 200 },
+    { label: '备注', prop: 'remarks', slot: 'remarks' },
+    {
+      columnKey: 'action',
+      label: '操作',
+      width: 180,
+      align: 'center',
+      slot: 'action',
+      hideInPrint: true,
+      hideInExport: true
+    }
+  ]);
+
+  /** 表格选中数据 */
+  const selections = ref([]);
+
+  /** 当前编辑数据 */
+  const current = ref(null);
+
+  /** 是否显示编辑弹窗 */
+  const showEdit = ref(false);
+
+  async function queryPage(params) {
+    const res = await proxy.$http.get(props.pageUrl, { params });
+    if (res.data.code === 200) {
+      return res.data;
+    }
+    return Promise.reject(new Error(res.data.msg));
+  }
+
+  /** 表格数据源 */
+  const datasource = ({ pages, where, orders }) => {
+    return queryPage({ ...where, ...orders, ...pages });
+  };
+
+  /** 搜索 */
+  const reload = (where) => {
+    tableRef.value?.reload?.({ page: 1, where });
+  };
+
+  /** 批量操作 */
+  const operatBatch = ({ row, url, title }) => {
+    const rows = row == null ? selections.value : [row];
+    if (!rows.length) {
+      EleMessage.error('请至少选择一条数据');
+      return;
+    }
+    title = title || '是否确认当前操作?';
+    ElMessageBox.confirm(title, '提示', {
+      type: 'warning',
+      draggable: true
+    })
+      .then(() => {
+        const loading = EleMessage.loading({
+          message: '请求中..',
+          plain: true
+        });
+
+        proxy.$http
+          .delete(url)
+          .then(() => {
+            loading.close();
+            EleMessage.success('操作成功');
+            reload();
+          })
+          .catch((e) => {
+            loading.close();
+            EleMessage.error(e.message);
+          });
+      })
+      .catch(() => {});
+  };
+
+  /// 导出数据
+  async function exportPage(params, name) {
+    const res = await proxy.$http({
+      url: props.exportUrl,
+      method: 'POST',
+      data: toFormData(params),
+      responseType: 'blob'
+    });
+    await checkDownloadRes(res);
+    download(
+      res.data,
+      name ? `${name}_${Date.now()}.xlsx` : `post_${Date.now()}.xlsx`
+    );
+  }
+
+  /** 导出数据 */
+  const exportData = (name) => {
+    const loading = EleMessage.loading({
+      message: '请求中..',
+      plain: true
+    });
+    tableRef.value?.fetch?.(({ where, orders }) => {
+      exportPage({ ...where, ...orders }, name)
+        .then(() => {
+          loading.close();
+        })
+        .catch((e) => {
+          loading.close();
+          EleMessage.error(e.message);
+        });
+    });
+  };
+
+  //修改备注
+  const remarksRef = ref(null);
+  function handleRemarks(row) {
+    remarksRef.value?.handleOpen(row);
+  }
+  const activities = [
+    {
+      content: 'Event start',
+      timestamp: '2018-04-15',
+      color: '#0bbd87',
+      icon: ChatDotSquare
+    },
+    {
+      content: 'Approved',
+      timestamp: '2018-04-13',
+      color: '#0bbd87',
+      icon: ChatDotSquare
+    },
+    {
+      content: 'Success',
+      timestamp: '2018-04-11',
+      color: '#0bbd87',
+      icon: ChatDotSquare
+    }
+  ];
+
+  defineExpose({ reload, exportData, operatBatch });
+</script>

+ 19 - 8
src/views/recycleOrder/components/user-bind-tag.vue

@@ -2,18 +2,13 @@
 <template>
   <ele-modal
     form
-    :width="460"
+    :width="520"
     v-model="visible"
     title="用户绑定标签"
     @open="handleOpen"
   >
     <el-checkbox-group v-model="userTag">
-      <el-checkbox label="书商" value="Value A" />
-      <el-checkbox label="合伙人" value="Value B" />
-      <el-checkbox label="内部账号" value="Value C" />
-      <el-checkbox label="优质书商" value="Value disabled" />
-      <el-checkbox label="大书商" value="Value selected and disabled" />
-      <el-checkbox label="分销客户" value="Value d" />
+      <el-checkbox v-for="item in userTagData" :label="item.tagName" :value="item.id" />
     </el-checkbox-group>
 
     <template #footer>
@@ -27,6 +22,7 @@
 
 <script setup>
   import { ref, reactive, nextTick } from 'vue';
+  import request from '@/utils/request'
 
   /** 弹窗是否打开 */
   const visible = ref(false);
@@ -42,8 +38,23 @@
   /** 弹窗打开事件 */
   const handleOpen = (row) => {
     visible.value = true;
-    nextTick(() => console.log('打开'));
+    nextTick(() => {
+        getUserTagList();
+    });
   };
+
+  const userTagData = ref([]);
+  //获取用户标签数据
+  const getUserTagList = () => {
+    request
+      .get('/user/tag/pagelist', { pageSize: 100, pageNum: 1 })
+      .then((res) => {
+        if (res.data.code == 200) {
+          userTagData.value = res.data.rows;
+        }
+      });
+  };
+
   /** 提交 */
   const handleSubmit = () => {
     loading.value = true;

+ 1 - 1
src/views/recycleOrder/search/index.vue

@@ -122,7 +122,7 @@
   import { ElMessageBox } from 'element-plus/es';
   import { EleMessage } from 'ele-admin-plus/es';
   import { DownloadOutlined } from '@/components/icons';
-  import OrderPage from '@/views/recycleOrder/components/order-page.vue';
+  import OrderPage from '@/views/recycleOrder/components/order-page-all.vue';
   import { useDictData } from '@/utils/use-dict-data';
   import { useRouter } from 'vue-router';
 

+ 2 - 2
src/views/statistic/defect/index.vue

@@ -37,8 +37,8 @@
     { type: 'index', label: '#', align: 'center', width: 80 },
     { label: '地区', prop: 'godownName', align: 'center' },
     { label: '收货仓库', prop: 'godownName', align: 'center' },
-    { label: '不良率', prop: 'godownName', align: 'center' }
-    { label: '统计日期', prop: 'createTime', align: 'center' },
+    { label: '不良率', prop: 'godownName', align: 'center' },
+    { label: '统计日期', prop: 'createTime', align: 'center' }
   ]);
 
   /** 页面组件实例 */