zouzs 1 napja
szülő
commit
a9384aa5e8

+ 0 - 21
src/App.vue

@@ -11,9 +11,6 @@ import { ElConfigProvider } from "element-plus";
 import { ReDialog } from "@/components/ReDialog";
 import zhCn from "element-plus/es/locale/lang/zh-cn";
 import plusZhCn from "plus-pro-components/es/locale/lang/zh-cn";
-import { getLocalDict } from "@/api/public";
-import { localForage } from "@/utils/localforage";
-import { refreshDictionaryCache } from "@/utils/dictionary";
 
 export default defineComponent({
   name: "app",
@@ -25,24 +22,6 @@ export default defineComponent({
     currentLocale() {
       return { ...zhCn, ...plusZhCn };
     }
-  },
-  mounted(): any {
-    this.getDictionary();
-  },
-  methods: {
-    async getDictionary() {
-      let res = await getLocalDict();
-      const dictionary: Record<string, Record<string, string>> = {};
-      res.data &&
-        res.data.forEach(({ key, code, msg, description }) => {
-          if (!(key in dictionary)) {
-            dictionary[key] = {};
-          }
-          dictionary[key][code] = msg;
-        });
-      await localForage().setItem("dictionary", dictionary);
-      await refreshDictionaryCache();
-    }
   }
 });
 </script>

+ 11 - 0
src/api/order.ts

@@ -54,6 +54,17 @@ export const merchantFollowInfoAllocationAccount = (data: object) => {
   );
 };
 
+// 获取我的订单列表(分页)
+export const getMyOrderInfoAllList = (data: object) => {
+  return http.request<Result>(
+    "post",
+    baseUrlApi("merchantOrderInfo/allocationMyList"),
+    {
+      data
+    }
+  );
+};
+
 type DetailResult = {
   code: number;
   success: boolean;

+ 4 - 1
src/router/modules/childAccount.ts

@@ -1,8 +1,11 @@
 // 最简代码,也就是这些字段必须有
+import { useUserStoreHook } from "@/store/modules/user";
+
 export default {
   path: "/childAccount",
   meta: {
-    title: "子账号管理"
+    title: "子账号管理",
+    showLink: !useUserStoreHook().isChild
   },
   children: [
     {

+ 5 - 2
src/router/modules/order.ts

@@ -1,4 +1,6 @@
 // 最简代码,也就是这些字段必须有
+import { useUserStoreHook } from "@/store/modules/user";
+
 export default {
   path: "/order",
   meta: {
@@ -11,13 +13,14 @@ export default {
       component: () => import("@/views/order/orderLoan/index.vue"),
       meta: {
         title: "全部订单",
-        showParent: true
+        showParent: true,
+        showLink: !useUserStoreHook().isChild
       }
     },
     {
       path: "/order/order-loan/detail",
       name: "OrderLoanDetail",
-      component: () => import("@/views/order/orderLoan/detail.vue"),
+      component: () => import("@/views/order/detail/detail.vue"),
       meta: {
         showLink: false,
         title: "订单详情",

+ 12 - 4
src/store/modules/user.ts

@@ -12,8 +12,7 @@ import {
   type RefreshTokenResult,
   getLogin,
   refreshTokenApi,
-  verifySmsLogin,
-  getUserInfo
+  verifySmsLogin
 } from "@/api/user";
 import { useMultiTagsStoreHook } from "./multiTags";
 import {
@@ -23,6 +22,7 @@ import {
   userKey,
   setUserInfo
 } from "@/utils/auth";
+import { getDictionaryList } from "@/utils/dictionary";
 
 export const useUserStore = defineStore("pure-user", {
   state: (): userType => ({
@@ -40,7 +40,9 @@ export const useUserStore = defineStore("pure-user", {
     // 是否勾选了登录页的免登录
     isRemembered: false,
     // 登录页的免登录存储几天,默认7天
-    loginDay: 7
+    loginDay: 7,
+    // 是否子账号
+    isChild: storageLocal().getItem<DataInfo<number>>(userKey)?.isChild ?? false
   }),
   actions: {
     /** 存储头像 */
@@ -71,15 +73,21 @@ export const useUserStore = defineStore("pure-user", {
     SET_LOGINDAY(value: number) {
       this.loginDay = Number(value);
     },
+    /** 存储是否子账号 */
+    SET_ISCHILD(bool: boolean) {
+      this.isChild = bool;
+    },
     /** 登入 */
     async loginByUsername(data: object, submitType: number) {
       return new Promise<UserResult>((resolve, reject) => {
         const loginfn = submitType === 1 ? getLogin : verifySmsLogin;
         loginfn(data)
           .then(res => {
-            if (res?.code === 0) {
+            if (res?.code === 0 || res?.code === 3) {
               setToken(res.data);
               setUserInfo(res.data);
+              // 系统特殊处理:获取全部字典
+              getDictionaryList();
             }
             resolve(res);
           })

+ 1 - 0
src/store/types.ts

@@ -44,4 +44,5 @@ export type userType = {
   permissions?: Array<string>;
   isRemembered?: boolean;
   loginDay?: number;
+  isChild?: boolean;
 };

+ 24 - 6
src/utils/auth.ts

@@ -19,6 +19,10 @@ export interface DataInfo<T> {
   roles?: Array<string>;
   /** 当前登录用户的按钮级别权限 */
   permissions?: Array<string>;
+  /** 是否子账号 */
+  main?: boolean;
+  /** 是否子账号 */
+  isChild?: boolean;
 
   admUserId: number;
   admUserName: string;
@@ -93,33 +97,46 @@ export function setToken(data: DataInfo<number>) {
 }
 
 export function setUserInfo(data: DataInfo<number>) {
-  function setUserKey({ avatar, username, nickname, roles, permissions }) {
+  function setUserKey({
+    avatar,
+    username,
+    nickname,
+    roles,
+    permissions,
+    isChild
+  }) {
     useUserStoreHook().SET_AVATAR(avatar);
     useUserStoreHook().SET_USERNAME(username);
     useUserStoreHook().SET_NICKNAME(nickname);
     useUserStoreHook().SET_ROLES(roles);
     useUserStoreHook().SET_PERMS(permissions);
+    useUserStoreHook().SET_ISCHILD(isChild);
     storageLocal().setItem(userKey, {
       // refreshToken,
       avatar,
       username,
       nickname,
       roles,
-      permissions
+      permissions,
+      isChild
     });
   }
 
-  if (data.permissions && data.roles) {
-    const { permissions, roles } = data;
+  if (data.admUserName && data.loginName) {
+    console.log("走的这里");
+    const { permissions, roles, main } = data;
+    console.log(main);
     const user = data.user;
     setUserKey({
       avatar: user?.avatar ?? "",
       username: user?.loginName ?? "",
       nickname: user?.admUserName ?? "",
       roles,
-      permissions
+      permissions,
+      isChild: !main
     });
   } else {
+    console.log("走这里");
     const avatar =
       storageLocal().getItem<DataInfo<number>>(userKey)?.avatar ?? "";
     const username =
@@ -135,7 +152,8 @@ export function setUserInfo(data: DataInfo<number>) {
       username,
       nickname,
       roles,
-      permissions
+      permissions,
+      isChild: !main
     });
   }
 }

+ 15 - 0
src/utils/dictionary.ts

@@ -1,4 +1,5 @@
 import { localForage } from "@/utils/localforage";
+import { getLocalDict } from "@/api/public";
 
 // 内存缓存,便于在 UI 格式化中同步返回
 let DICT_CACHE = null;
@@ -13,6 +14,20 @@ localForage()
     DICT_CACHE = null;
   });
 
+export const getDictionaryList = async () => {
+  const res = await getLocalDict();
+  const dictionary: Record<string, Record<string, string>> = {};
+  res.data &&
+    res.data.forEach(({ key, code, msg }) => {
+      if (!(key in dictionary)) {
+        dictionary[key] = {};
+      }
+      dictionary[key][code] = msg;
+    });
+  await localForage().setItem("dictionary", dictionary);
+  await refreshDictionaryCache();
+};
+
 /**
  * 获取字典显示值(同步)
  * @param type 字典类型

+ 6 - 0
src/views/childAccount/index.vue

@@ -70,6 +70,8 @@ import {
   merchantUserInfoupdateStatus,
   updateChildAccount
 } from "@/api/childAccount";
+import { AesEncode } from "@/utils/crypto";
+import { md5 } from "js-md5";
 
 defineOptions({
   name: "ChildAccount"
@@ -313,6 +315,8 @@ const handleSubmit = async (values: FieldValues) => {
   if (state.isCreate) {
     try {
       let params = form.value;
+      params.contractPassword = md5(form.value.contractPassword).toUpperCase();
+      params.confirmPassword = md5(form.value.confirmPassword).toUpperCase();
       let res = await createChildAccount(params);
       if (res.code === 0) {
         ElMessage.success("新增成功");
@@ -329,6 +333,8 @@ const handleSubmit = async (values: FieldValues) => {
     // 编辑
     try {
       let params = form.value;
+      params.contractPassword = md5(form.value.contractPassword).toUpperCase();
+      params.confirmPassword = md5(form.value.confirmPassword).toUpperCase();
       let res = await updateChildAccount(params);
       if (res.code === 0) {
         ElMessage.success("修改成功");

+ 14 - 6
src/views/login/index.vue

@@ -156,24 +156,32 @@ const onLogin = async (formEl: FormInstance | undefined) => {
               type: "success",
               duration: 1000,
               onClose() {
+                // 全部采取静态路由模式
+                usePermissionStoreHook().handleWholeMenus([]);
+                addPathMatch();
                 if (code === 3) {
                   ElMessage.warning(
                     "为了保障您的账号信息安全,请修改登录密码,需包含8位数字、字母、特殊字符"
                   );
-                  // 去修改密码页
-                  router.replace("/system/pwd_reset");
+                  const toPath = decodeURIComponent(
+                    (route.query?.redirect || "/") as string
+                  );
+                  if (route.name === "Login") {
+                    /*initRouter().then(() => {
+                      router.push(getTopMenu(true).path);
+                    });*/
+                    console.log("getTopMenu", getTopMenu(true).path);
+                    router.push(getTopMenu(true).path);
+                    message("登录成功", { type: "success" });
+                  } else router.replace(toPath);
                 } else {
                   const toPath = decodeURIComponent(
                     (route.query?.redirect || "/") as string
                   );
-                  console.log(route.name);
                   if (route.name === "Login") {
                     /*initRouter().then(() => {
                       router.push(getTopMenu(true).path);
                     });*/
-                    // 全部采取静态路由模式
-                    usePermissionStoreHook().handleWholeMenus([]);
-                    addPathMatch();
                     console.log("getTopMenu", getTopMenu(true).path);
                     router.push(getTopMenu(true).path);
                     message("登录成功", { type: "success" });

+ 0 - 0
src/views/order/orderLoan/detail.vue → src/views/order/detail/detail.vue


+ 530 - 4
src/views/order/orderMy/index.vue

@@ -1,9 +1,535 @@
-<script setup lang="ts">
+<template>
+  <div>
+    <PlusPage
+      ref="plusPageInstance"
+      :columns="tableConfig"
+      :request="getList"
+      :is-card="true"
+      :search="{
+        labelWidth: 100,
+        showNumber: 3
+      }"
+      :table="{
+        actionBar: { buttons, type: 'link', width: 180 },
+        adaptive: { offsetBottom: 50 },
+        isSelection: true,
+        rowKey: 'id',
+        onSelectionChange: handleSelectionChange
+      }"
+      :pageInfoMap="{ page: 'pageNum', pageSize: 'pageSize' }"
+    >
+      <template #table-title>
+        <el-row class="button-row">
+          <el-button type="danger" @click="handleReliveOrder">
+            解除分配
+          </el-button>
+          <el-button type="primary" @click="handleReset"> 重新分配 </el-button>
+        </el-row>
+      </template>
+    </PlusPage>
+    <!-- 弹窗编辑 -->
+    <PlusDialogForm
+      ref="dialogForm"
+      v-model="form"
+      v-model:visible="dialogVisible"
+      :form="{
+        columns,
+        labelPosition: 'left',
+        labelWidth: 100,
+        rules
+      }"
+      :dialog="{ title: dialogTitle + '菜单', width: 600, confirmLoading }"
+      @confirm="handleSubmit"
+      @close="handleClose"
+    />
+    <!-- 跟进状态弹窗 -->
+    <plusDialog
+      v-model="detailsVisible"
+      :hasFooter="false"
+      title="跟进状态"
+      width="600"
+    >
+      <plus-descriptions
+        :column="4"
+        :data="detailForm"
+        :columns="detailsColumns"
+        :border="false"
+      />
+      <el-timeline>
+        <el-timeline-item
+          v-for="item in detailForm.list"
+          :key="item.id"
+          :timestamp="item.createTime"
+        >
+          <el-card>
+            <el-row>
+              <el-col :span="8">
+                <el-rate v-model="item.starLevel" disabled />
+              </el-col>
+              <el-col :span="16">
+                <el-tag>{{ item.remark }}</el-tag>
+              </el-col>
+            </el-row>
+          </el-card>
+        </el-timeline-item>
+      </el-timeline>
+    </plusDialog>
+    <!-- 重新分配弹窗 -->
+    <PlusDialogForm
+      ref="resetFormDialog"
+      v-model="resetForm"
+      v-model:visible="resetVisible"
+      :form="{
+        columns: resetColumns,
+        labelWidth: 100,
+        rules: resetRules
+      }"
+      title="重新分配"
+      :dialog="{
+        width: 400,
+        confirmLoading
+      }"
+      @confirm="handleResetAccount"
+      @close="handleClose"
+    />
+  </div>
+</template>
+
+<script lang="ts" setup>
+import {
+  computed,
+  onMounted,
+  reactive,
+  ref,
+  resolveDirective,
+  toRefs
+} from "vue";
+import type { FormRules } from "element-plus";
+import { ElMessage, ElMessageBox } from "element-plus";
+import {
+  type FieldValues,
+  type PlusColumn,
+  PlusDescriptions,
+  PlusDialog,
+  PlusDialogForm,
+  PlusPage,
+  PlusPageInstance,
+  useTable
+} from "plus-pro-components";
+import { useRouter } from "vue-router";
+import {
+  getMerchantOrderInfoAllList,
+  getMyOrderInfoAllList,
+  merchantFollowInfoAllocationAccount,
+  merchantFollowInfoList,
+  merchantFollowInfoReliveOrder,
+  updateStartLevel
+} from "@/api/order";
+import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
+import { isString } from "@pureadmin/utils";
+import { getChildAccount } from "@/api/childAccount";
+
 defineOptions({
   name: "OrderMy"
 });
-</script>
 
-<template></template>
+const router = useRouter();
+
+const plusPageInstance = ref<PlusPageInstance | null>(null);
+
+const getList = async (query: Record<string, any>) => {
+  let res = await getMyOrderInfoAllList(query);
+  return {
+    data: res.data.list,
+    total: res.data.total
+  };
+};
+
+onMounted(() => {
+  getChildAccountList();
+});
+
+// 重新请求列表接口
+const refresh = () => {
+  plusPageInstance.value?.getList();
+};
+
+const dialogTitle = computed(() => (state.isCreate ? "新增" : "编辑"));
+
+const multipleSelection = ref([]);
+
+const handleSelectionChange = (val: any[]) => {
+  multipleSelection.value = val;
+};
+
+const handleReliveOrder = () => {
+  if (multipleSelection.value.length === 0) {
+    ElMessage.warning("请选择要解除分配的订单");
+    return;
+  }
+  ElMessageBox.confirm("确认解除分配?", "提示").then(async () => {
+    try {
+      let res = await merchantFollowInfoReliveOrder({
+        orderIds: multipleSelection.value.map((item: any) => item.id)
+      });
+      if (res.code === 0) {
+        ElMessage.success("解除分配成功");
+        refresh();
+      }
+    } catch (e) {
+      ElMessage.error("删除失败");
+    }
+  });
+};
+
+// 表格数据
+const tableConfig: PlusColumn[] = [
+  {
+    label: "订单号",
+    tableColumnProps: {
+      showOverflowTooltip: true
+    },
+    width: 150,
+    prop: "orderNo"
+  },
+  {
+    label: "客户姓名",
+    prop: "userName"
+  },
+  {
+    label: "性别",
+    prop: "userSex",
+    width: 60,
+    hideInSearch: true
+  },
+  {
+    label: "客户星级",
+    prop: "star",
+    valueType: "rate",
+    editable: true,
+    width: 140,
+    fieldProps: {
+      disabled: true
+    },
+    hideInSearch: true
+  },
+  {
+    label: "电话",
+    prop: "maskPhone",
+    width: 100,
+    hideInSearch: true
+  },
+  {
+    label: "微信",
+    prop: "wxCode",
+    tableColumnProps: {
+      showOverflowTooltip: true
+    },
+    hideInSearch: true
+  },
+  {
+    label: "优先联系方式",
+    prop: "headContractType",
+    hideInSearch: true
+  },
+  {
+    label: "申请产品",
+    prop: "planName",
+    hideInSearch: true
+  },
+  {
+    label: "需求资金",
+    prop: "loanAmount",
+    hideInSearch: true
+  },
+  {
+    label: "贷款期限",
+    prop: "loanTerm",
+    hideInSearch: true
+  },
+  {
+    label: "申请时间",
+    prop: "applyTime",
+    width: 170,
+    hideInSearch: true
+  },
+  {
+    label: "分配子账号",
+    prop: "follower",
+    width: 120,
+    hideInSearch: true
+  },
+  {
+    label: "状态",
+    prop: "followStatus",
+    valueType: "select",
+    options: [
+      { label: "未跟进", value: 0 },
+      { label: "跟进", value: 4 }
+    ],
+    hideInSearch: true
+  },
+  {
+    label: "备注",
+    prop: "wechat",
+    hideInSearch: true
+  }
+];
+
+/*--------------------表单--------------------*/
+
+// 表单实例
+const dialogForm = ref(null);
+
+interface State {
+  dialogVisible: boolean;
+  detailsVisible: boolean;
+  resetVisible: boolean;
+  confirmLoading: boolean;
+  selectedIds: number[];
+  isCreate: boolean;
+  form: {};
+  detailForm: {
+    list: any;
+  };
+  resetForm: {};
+  rules: FormRules;
+  resetRules: FormRules;
+}
 
-<style scoped lang="scss"></style>
+const state = reactive<State>({
+  dialogVisible: false,
+  detailsVisible: false,
+  resetVisible: false,
+  confirmLoading: false,
+  selectedIds: [],
+  isCreate: false,
+  form: {},
+  detailForm: {
+    list: []
+  },
+  resetForm: {},
+  rules: {
+    starLevel: [
+      { required: true, message: "请选择客户星级", trigger: "change" }
+    ]
+  },
+  resetRules: {
+    accountId: [
+      { required: true, message: "请选择分配子账号", trigger: "change" }
+    ]
+  }
+});
+
+const columns: PlusColumn[] = [
+  {
+    label: "客户星级",
+    prop: "starLevel",
+    valueType: "select",
+    tooltip: {
+      content:
+        "<span>客户星级说明</span></br>" +
+        "<span>0星:未接通</span></br>" +
+        "<span>1星:接通但无办理意向</span></br>" +
+        "<span>2星:有意向但无可贷点</span></br>" +
+        "<span>3星:有可贷点但资质一般</span></br>" +
+        "<span>4星:有可贷点且条件较好</span></br>" +
+        "<span>5星:马上需要或条件优质</span></br>",
+      rawContent: true
+    },
+    options: [
+      { label: "1星", value: 1 },
+      { label: "2星", value: 2 },
+      { label: "3星", value: 3 },
+      { label: "4星", value: 4 },
+      { label: "5星", value: 5 }
+    ]
+  },
+  {
+    label: "备注",
+    prop: "remark",
+    valueType: "input",
+    fieldProps: {
+      type: "textarea",
+      rows: 4,
+      maxlength: 200,
+      showWordLimit: true
+    }
+  }
+];
+
+const detailsColumns: PlusColumn[] = [
+  {
+    label: "姓名:",
+    prop: "userName"
+  },
+  {
+    label: "需求资金:",
+    prop: "loanAmount"
+  },
+  {
+    label: "年龄:",
+    prop: "age"
+  },
+  {
+    label: "需求周期:",
+    prop: "loanTerm"
+  }
+];
+
+const childAccountList = ref<any[]>([]);
+
+const getChildAccountList = async () => {
+  let res = await getChildAccount();
+  childAccountList.value = res.data;
+};
+
+const resetColumns: PlusColumn[] = [
+  {
+    label: "分配子账号",
+    prop: "accountId",
+    valueType: "select",
+    options: computed(() => childAccountList.value),
+    optionsMap: {
+      label: "contractName",
+      value: "id"
+    }
+  }
+];
+
+const handleSubmit = async (values: FieldValues) => {
+  confirmLoading.value = true;
+  try {
+    let params = form.value;
+    console.log(params);
+    let res = await updateStartLevel(params);
+    if (res.code === 0) {
+      ElMessage.success("修改成功");
+      confirmLoading.value = false;
+      dialogVisible.value = false;
+      refresh();
+    } else {
+      ElMessage.error(res.msg);
+    }
+  } finally {
+    confirmLoading.value = false;
+  }
+};
+
+const handleResetAccount = async (values: FieldValues) => {
+  confirmLoading.value = true;
+  try {
+    let params = resetForm.value;
+    console.log(params);
+    let res = await merchantFollowInfoAllocationAccount(params);
+    if (res.code === 0) {
+      ElMessage.success("重新分配成功");
+      resetVisible.value = false;
+      refresh();
+    } else {
+      ElMessage.error(res.msg);
+    }
+  } catch (e) {
+    ElMessage.error("重新分配失败");
+  } finally {
+    confirmLoading.value = false;
+  }
+};
+
+const handleClose = () => {
+  // 重置表单校验状态
+  console.log(dialogForm.value.formInstance);
+};
+
+const { buttons } = useTable();
+
+const perms = resolveDirective("perms");
+
+const handleReset = () => {
+  if (multipleSelection.value.length === 0) {
+    ElMessage.warning("请选择要重新分配的订单");
+    return;
+  }
+  resetForm.value = {};
+  resetForm.value.id = multipleSelection.value.map((item: any) => item.id);
+  console.log(resetForm.value);
+  resetVisible.value = true;
+};
+
+buttons.value = [
+  {
+    // 修改
+    text: "详情",
+    code: "edit",
+    // props v0.1.16 版本新增函数类型
+    props: {
+      type: "primary"
+    },
+    onClick(val) {
+      let params = {
+        id: val.row.id
+      };
+      Object.keys(params).forEach(param => {
+        if (!isString(params[param])) {
+          params[param] = params[param].toString();
+        }
+      });
+      // 保存信息到标签页
+      useMultiTagsStoreHook().handleTags("push", {
+        path: `/order/order-loan/detail`,
+        name: "OrderLoanDetail",
+        query: params,
+        meta: {
+          title: `订单详情`,
+          // 如果使用的是非国际化精简版title可以像下面这么写
+          // title: `No.${index} - 详情信息`,
+          // 最大打开标签数
+          dynamicLevel: 1
+        }
+      });
+      // 路由跳转
+      router.push({ name: "OrderLoanDetail", query: params });
+    }
+  },
+  {
+    // 跟进
+    text: "跟进",
+    props: {
+      type: "primary"
+    },
+    onClick(val) {
+      form.value = {};
+      form.value.orderId = val.row.orderNo;
+      dialogVisible.value = true;
+    }
+  },
+  {
+    // 修改
+    text: "跟进状态",
+    props: {
+      type: "primary"
+    },
+    onClick(val: any) {
+      console.log(val.row);
+      detailForm.value = val.row;
+      detailForm.value.list = [];
+      merchantFollowInfoList({ orderNo: val.row.orderNo }).then(res => {
+        detailForm.value.list = res.data;
+      });
+      detailsVisible.value = true;
+    }
+  }
+];
+
+const {
+  form,
+  detailForm,
+  resetForm,
+  confirmLoading,
+  rules,
+  resetRules,
+  dialogVisible,
+  detailsVisible,
+  resetVisible
+} = toRefs(state);
+</script>