zouzs 2 napja
szülő
commit
4f8fd9aa15

+ 21 - 0
src/App.vue

@@ -11,6 +11,9 @@ 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",
@@ -22,6 +25,24 @@ 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>

+ 36 - 1
src/api/order.ts

@@ -5,30 +5,34 @@ type Result = {
   code: number;
   success: boolean;
   data: {
-    list: any[];
+    list: object[];
     total: number;
   };
   msg: string;
 };
 
+// 获取订单列表(分页)
 export const getMerchantOrderInfoAllList = (data: object) => {
   return http.request<Result>("post", baseUrlApi("merchantOrderInfo/allList"), {
     data
   });
 };
 
+// 添加订单
 export const updateStartLevel = (data: object) => {
   return http.request<Result>("post", baseUrlApi("merchantFollowInfo/add"), {
     data
   });
 };
 
+// 获取订单列表
 export const merchantFollowInfoList = (data: object) => {
   return http.request<Result>("post", baseUrlApi("merchantFollowInfo/list"), {
     data
   });
 };
 
+// 解除分配
 export const merchantFollowInfoReliveOrder = (data: object) => {
   return http.request<Result>(
     "post",
@@ -38,3 +42,34 @@ export const merchantFollowInfoReliveOrder = (data: object) => {
     }
   );
 };
+
+// 分配订单
+export const merchantFollowInfoAllocationAccount = (data: object) => {
+  return http.request<Result>(
+    "post",
+    baseUrlApi("merchantOrderInfo/allocationAccount"),
+    {
+      data
+    }
+  );
+};
+
+type DetailResult = {
+  code: number;
+  success: boolean;
+  data: {
+    custAssertInfoVO: object;
+    orderInfoVO: object;
+  };
+};
+
+// 详情
+export const merchantFollowInfoDetail = (data: object) => {
+  return http.request<DetailResult>(
+    "post",
+    baseUrlApi("merchantOrderInfo/detail"),
+    {
+      data
+    }
+  );
+};

+ 2 - 4
src/api/public.ts

@@ -6,8 +6,6 @@ type Result = {
   data: Array<any>;
 };
 
-export const getLocalDict = data => {
-  return http.request<Result>("post", baseUrlApi("crmSystem/getLocalDict"), {
-    data
-  });
+export const getLocalDict = () => {
+  return http.request<Result>("post", baseUrlApi("crmSystem/getLocalDict"));
 };

+ 53 - 0
src/utils/dictionary.ts

@@ -0,0 +1,53 @@
+import { localForage } from "@/utils/localforage";
+
+// 内存缓存,便于在 UI 格式化中同步返回
+let DICT_CACHE = null;
+
+// 模块加载后尝试初始化缓存(无需等待)
+localForage()
+  .getItem("dictionary")
+  .then(dict => {
+    DICT_CACHE = dict || null;
+  })
+  .catch(() => {
+    DICT_CACHE = null;
+  });
+
+/**
+ * 获取字典显示值(同步)
+ * @param type 字典类型
+ * @param code 字典值(字符串/数字)
+ * @param opts 可选项:fallback占位符,showRawWhenMiss未命中时显示原值
+ */
+export const getDictionary = (
+  type: string,
+  code: string | number | null | undefined,
+  opts: { fallback?: string; showRawWhenMiss?: boolean } = {}
+): string => {
+  const fallback = opts.fallback ?? "";
+  if (code === null || code === undefined || code === "") return fallback;
+
+  const key = String(code);
+  const dictByType = DICT_CACHE?.[type];
+
+  console.log(DICT_CACHE);
+
+  if (dictByType && key in dictByType) {
+    return dictByType[key] ?? fallback;
+  }
+
+  // 缓存未命中:按需返回原始值或占位
+  return opts.showRawWhenMiss ? key : fallback;
+};
+
+/**
+ * 重新刷新内存缓存(可在 App 初始化字典后调用)
+ */
+export const refreshDictionaryCache = async () => {
+  try {
+    const dict = await localForage().getItem("dictionary");
+    DICT_CACHE = dict || null;
+  } catch {
+    DICT_CACHE = null;
+  }
+};

+ 164 - 1
src/views/order/orderLoan/detail.vue

@@ -1,11 +1,174 @@
 <script setup lang="ts">
+import { type PlusColumn, PlusDescriptions } from "plus-pro-components";
+import { useRoute } from "vue-router";
+import { onMounted, ref } from "vue";
+import { merchantFollowInfoDetail } from "@/api/order";
+import { getDictionary } from "@/utils/dictionary";
+
 defineOptions({
   name: "OrderLoanDetail"
 });
+
+const route = useRoute();
+console.log(route.query.id);
+
+onMounted(() => {
+  getDetail(route.query.id as string);
+});
+
+const getDetail = async (id: string) => {
+  let res = await merchantFollowInfoDetail({ id });
+  data.value = Object.assign(
+    {},
+    res.data.custAssertInfoVO,
+    res.data.orderInfoVO
+  );
+  console.log(data.value);
+};
+
+const data = ref();
+
+const columns: PlusColumn[] = [
+  { label: "姓名:", prop: "userName" },
+  { label: "年龄:", prop: "age" },
+  { label: "性别:", prop: "userSex" },
+  { label: "客户星级:", prop: "star", formatter: val => `${val ? val : ""}星` },
+  { label: "需求资金:", prop: "loanAmount" },
+  { label: "需求周期:", prop: "loanTerm", formatter: val => `${val}个月` }
+];
+
+const columns2: PlusColumn[] = [
+  { label: "电话:", prop: "phone" },
+  { label: "微信:", prop: "wxCode" }
+];
+
+const columns3: PlusColumn[] = [
+  { label: "工作所在地:", prop: "city" },
+  {
+    label: "文化程度:",
+    prop: "education",
+    formatter: val => getDictionary("CultureLevel", val)
+  },
+  {
+    label: "社保情况:",
+    prop: "socialSecurity",
+    formatter: val => getDictionary("SociaSecurity", val)
+  },
+  {
+    label: "公积金情况:",
+    prop: "fund",
+    formatter: val => getDictionary("PublicFund", val)
+  },
+  {
+    label: "芝麻分区间:",
+    prop: "zhima",
+    formatter: val => getDictionary("ZhiMa", val)
+  }
+];
+
+const columns4: PlusColumn[] = [
+  {
+    label: "职业:",
+    prop: "career",
+    formatter: val => getDictionary("ProfessionList", val)
+  },
+  {
+    label: "月收入:",
+    prop: "monthIncome",
+    formatter: val => getDictionary("MouthIncome", val)
+  },
+  {
+    label: "工资支付方式:",
+    prop: "salaryType",
+    formatter: val => getDictionary("WorkPayment", val)
+  }
+];
+
+const columns5: PlusColumn[] = [
+  {
+    label: "车产:",
+    prop: "carSituation",
+    formatter: val => getDictionary("CarSituation", val)
+  },
+  {
+    label: "房产:",
+    prop: "house",
+    formatter: val => getDictionary("HouseSituation", val)
+  },
+  {
+    label: "寿险保单:",
+    prop: "insurance",
+    formatter: val => getDictionary("Insurance", val)
+  },
+  { label: "车牌号:", prop: "carNo" },
+  { label: "房产所在城市:", prop: "houseCity" },
+  {
+    label: "购车方式:",
+    prop: "buyCarWay",
+    formatter: val => getDictionary("CarBuyType", val)
+  },
+  {
+    label: "购房方式:",
+    prop: "housePurchaseMethod",
+    formatter: val => getDictionary("HouseBuyType", val)
+  }
+];
 </script>
 
 <template>
-  <div>321321</div>
+  <div>
+    <el-card header-class="h-10 p-2!" shadow="always" header="本地贷款订单详情">
+      <el-card
+        header-class="h-10 p-2!"
+        body-class="p-4!"
+        shadow="never"
+        header="订单信息"
+      >
+        <PlusDescriptions :columns="columns" :data="data" :border="false" />
+      </el-card>
+      <el-card
+        header-class="h-10 p-2!"
+        body-class="p-4!"
+        shadow="never"
+        class="mt-5"
+        header="联系方式"
+      >
+        <PlusDescriptions :columns="columns2" :data="data" :border="false" />
+      </el-card>
+      <el-card
+        header-class="h-10 p-2!"
+        body-class="p-4!"
+        shadow="never"
+        class="mt-5"
+        header="基础信息"
+      >
+        <PlusDescriptions :columns="columns3" :data="data" :border="false" />
+      </el-card>
+      <el-card
+        header-class="h-10 p-2!"
+        body-class="p-4!"
+        shadow="never"
+        class="mt-5"
+        header="职业信息"
+      >
+        <PlusDescriptions :columns="columns4" :data="data" :border="false" />
+      </el-card>
+      <el-card
+        header-class="h-10 p-2!"
+        body-class="p-4!"
+        shadow="never"
+        class="mt-5"
+        header="职业信息"
+      >
+        <PlusDescriptions :columns="columns5" :data="data" :border="false" />
+      </el-card>
+      <el-row class="mt-5">
+        <el-col :span="12">
+          <el-button type="primary" @click="$router.back()">返回</el-button>
+        </el-col>
+      </el-row>
+    </el-card>
+  </div>
 </template>
 
 <style scoped lang="scss"></style>

+ 46 - 28
src/views/order/orderLoan/index.vue

@@ -42,6 +42,7 @@
       @confirm="handleSubmit"
       @close="handleClose"
     />
+    <!-- 跟进状态弹窗 -->
     <plusDialog
       v-model="detailsVisible"
       :hasFooter="false"
@@ -63,7 +64,7 @@
           <el-card>
             <el-row>
               <el-col :span="8">
-                <el-rate v-model="item.starLevel" />
+                <el-rate v-model="item.starLevel" disabled />
               </el-col>
               <el-col :span="16">
                 <el-tag>{{ item.remark }}</el-tag>
@@ -73,16 +74,23 @@
         </el-timeline-item>
       </el-timeline>
     </plusDialog>
+    <!-- 重新分配弹窗 -->
     <PlusDialogForm
-      ref="resetForm"
+      ref="resetFormDialog"
       v-model="resetForm"
       v-model:visible="resetVisible"
-      :form="{ columns: resetColumns, confirmLoading, labelWidth: 100 }"
+      :form="{
+        columns: resetColumns,
+        labelWidth: 100,
+        rules: resetRules
+      }"
       title="重新分配"
       :dialog="{
-        width: 400
+        width: 400,
+        confirmLoading
       }"
       @confirm="handleResetAccount"
+      @close="handleClose"
     />
   </div>
 </template>
@@ -111,6 +119,7 @@ import {
 import { useRouter } from "vue-router";
 import {
   getMerchantOrderInfoAllList,
+  merchantFollowInfoAllocationAccount,
   merchantFollowInfoList,
   merchantFollowInfoReliveOrder,
   updateStartLevel
@@ -172,15 +181,6 @@ const handleReliveOrder = () => {
   });
 };
 
-const handleReset = () => {
-  if (multipleSelection.value.length === 0) {
-    ElMessage.warning("请选择要重新分配的订单");
-    return;
-  }
-  resetForm.value.ids = multipleSelection.value.map((item: any) => item.id);
-  resetVisible.value = true;
-};
-
 // 表格数据
 const tableConfig: PlusColumn[] = [
   {
@@ -254,7 +254,7 @@ const tableConfig: PlusColumn[] = [
   },
   {
     label: "分配子账号",
-    prop: "belongName",
+    prop: "follower",
     width: 120,
     hideInSearch: true
   },
@@ -289,12 +289,11 @@ interface State {
   isCreate: boolean;
   form: {};
   detailForm: {
-    list: any[];
-  };
-  resetForm: {
-    ids: any[];
+    list: any;
   };
+  resetForm: {};
   rules: FormRules;
+  resetRules: FormRules;
 }
 
 const state = reactive<State>({
@@ -308,11 +307,16 @@ const state = reactive<State>({
   detailForm: {
     list: []
   },
-  resetForm: {
-    ids: []
-  },
+  resetForm: {},
   rules: {
-    star: [{ required: true, message: "请选择客户星级", trigger: "blur" }]
+    starLevel: [
+      { required: true, message: "请选择客户星级", trigger: "change" }
+    ]
+  },
+  resetRules: {
+    accountId: [
+      { required: true, message: "请选择分配子账号", trigger: "change" }
+    ]
   }
 });
 
@@ -382,7 +386,7 @@ const getChildAccountList = async () => {
 const resetColumns: PlusColumn[] = [
   {
     label: "分配子账号",
-    prop: "id",
+    prop: "accountId",
     valueType: "select",
     options: computed(() => childAccountList.value),
     optionsMap: {
@@ -393,10 +397,10 @@ const resetColumns: PlusColumn[] = [
 ];
 
 const handleSubmit = async (values: FieldValues) => {
-  console.log(values, "Submit");
   confirmLoading.value = true;
   try {
     let params = form.value;
+    console.log(params);
     let res = await updateStartLevel(params);
     if (res.code === 0) {
       ElMessage.success("修改成功");
@@ -412,19 +416,20 @@ const handleSubmit = async (values: FieldValues) => {
 };
 
 const handleResetAccount = async (values: FieldValues) => {
-  console.log(values, "Submit");
   confirmLoading.value = true;
   try {
     let params = resetForm.value;
-    let res = await merchantFollowInfoReliveOrder(params);
+    console.log(params);
+    let res = await merchantFollowInfoAllocationAccount(params);
     if (res.code === 0) {
       ElMessage.success("重新分配成功");
-      confirmLoading.value = false;
       resetVisible.value = false;
       refresh();
     } else {
       ElMessage.error(res.msg);
     }
+  } catch (e) {
+    ElMessage.error("重新分配失败");
   } finally {
     confirmLoading.value = false;
   }
@@ -439,6 +444,17 @@ 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 = [
   {
     // 修改
@@ -481,6 +497,7 @@ buttons.value = [
       type: "primary"
     },
     onClick(val) {
+      form.value = {};
       form.value.orderId = val.row.orderNo;
       dialogVisible.value = true;
     }
@@ -491,7 +508,7 @@ buttons.value = [
     props: {
       type: "primary"
     },
-    onClick(val) {
+    onClick(val: any) {
       console.log(val.row);
       detailForm.value = val.row;
       detailForm.value.list = [];
@@ -509,6 +526,7 @@ const {
   resetForm,
   confirmLoading,
   rules,
+  resetRules,
   dialogVisible,
   detailsVisible,
   resetVisible