Kaynağa Gözat

1.新增组件日期选择器(picker用法)、日期选择器(日历用法)、多选下拉列表组件
2.优化单选下拉列表组件
3.新增土规下单
4.优化个贷和大中型下单

GouGengquan 1 ay önce
ebeveyn
işleme
f55a5315af

+ 11 - 0
src/api/customer.js

@@ -0,0 +1,11 @@
+import request from '@/utils/request'
+
+// 客户下拉列表
+export function simpleAll() {
+    return request.get(`customer/simpleAll`)
+}
+
+// 根据id获取客户详情
+export function detail(params) {
+    return request.get(`customer/${params}`)
+}

+ 6 - 0
src/api/department.js

@@ -0,0 +1,6 @@
+import request from '@/utils/request'
+
+// 部门下拉列表
+export function simpleAll(params) {
+    return request.get(`department/simpleAll`, { params: params })
+}

+ 6 - 0
src/api/dictData.js

@@ -0,0 +1,6 @@
+import request from '@/utils/request'
+
+// 字典数据下拉列表
+export function simpleType(params) {
+    return request.get(`dictData/simple/${params}`)
+}

+ 6 - 0
src/api/land.js

@@ -0,0 +1,6 @@
+import request from '@/utils/request'
+
+// 土规下单
+export function add(params) {
+    return request.post(`item`, params)
+}

+ 4 - 0
src/api/user.js

@@ -25,4 +25,8 @@ export function modifyPass(params) {
         lastPassword: md5(params.lastPassword),
         newPassword: md5(params.newPassword),
     })
+}
+
+export function simpleAll() {
+    return request.get(`user/simpleAll`)
 }

+ 94 - 0
src/components/VanCalendar/index.vue

@@ -0,0 +1,94 @@
+<template>
+  <div>
+    <van-field v-model="text" is-link readonly :name="name" :label="label" placeholder="点击选择日期" @click="showCalendar = true" :rules="rules" :required="required" />
+    <van-calendar v-model:show="showCalendar" @confirm="onConfirm" switch-mode="year-month" :type="type" :title="title" />
+  </div>
+</template>
+
+<script>
+export default {
+  props: {
+    // 表单项
+    label: String,
+    // 组件name
+    name: String,
+    // 绑定值(该组件可以返回多个日期的数组)
+    modelValue: [String, Array],
+    // 校验规则
+    rules: Array,
+    // 是否显示*标
+    required: Boolean,
+    // 最大日期
+    minDate: Date,
+    // 最小日期
+    maxDate: Date,
+    // 空间类型(multiple: 多个日期, range: 日期区间, 这两种类型会返回数组 详见官方文档)
+    type: String,
+    // 组件标题
+    title: String,
+  },
+  data() {
+    return {
+      // 显示的文本
+      text: '',
+      // 实际的日期
+      date: null,
+      // 组件可见状态
+      showCalendar: false,
+    };
+  },
+  watch: {
+    date(newVal) {
+      // 更新绑定值
+      this.$emit('update:modelValue', newVal);
+      // 传回change事件
+      this.$emit('change', newVal);
+    },
+  },
+  mounted() {
+    if (!this.type) {
+      this.date = '';
+    } else {
+      this.date = [];
+    }
+  },
+  methods: {
+    onConfirm(value) {
+      // 格式化日期格式
+      if (!this.type) {
+        // 单个日期选择
+        let date = new Date(value);
+        let y = date.getFullYear();
+        let m = date.getMonth() + 1;
+        m = m < 10 ? '0' + m : m;
+        let d = date.getDate();
+        d = d < 10 ? '0' + d : d;
+        // TODO: 默认横线连接, 后面再看要不要支持别的格式
+        const time = y + '-' + m + '-' + d;
+        this.date = time;
+        // 单个日期直接回显
+        this.text = this.date;
+      } else {
+        for (let index = 0; index < value.length; index++) {
+          const element = value[index];
+          let date = new Date(element);
+          let y = date.getFullYear();
+          let m = date.getMonth() + 1;
+          m = m < 10 ? '0' + m : m;
+          let d = date.getDate();
+          d = d < 10 ? '0' + d : d;
+          // TODO: 默认横线连接, 后面再看要不要支持别的格式
+          const time = y + '-' + m + '-' + d;
+          this.date.push(time);
+        }
+        // 日期区间处理一下, 选择多个日期就不处理了, 默认就是 ‘,’ 分割
+        if (this.type === 'range') {
+          this.text = this.date[0] + ' 至 ' + this.date[1];
+        }
+      }
+
+      this.showCalendar = false;
+    },
+  },
+};
+</script>

+ 54 - 0
src/components/VanDatePicker/index.vue

@@ -0,0 +1,54 @@
+<template>
+  <div>
+    <van-field v-model="date" is-link readonly :name="name" :label="label" placeholder="点击选择时间" @click="showPicker = true" :rules="rules" :required="required" />
+    <van-popup v-model:show="showPicker" destroy-on-close position="bottom">
+      <van-date-picker :model-value="pickerValue" :min-date="minDate" :max-date="maxDate" @confirm="onConfirm" @cancel="showPicker = false" />
+    </van-popup>
+  </div>
+</template>
+
+<script>
+export default {
+  props: {
+    // 表单项
+    label: String,
+    // 组件name
+    name: String,
+    // 绑定值
+    modelValue: String,
+    // 校验规则
+    rules: Array,
+    // 是否显示*标
+    required: Boolean,
+    // 最大日期
+    minDate: Date,
+    // 最小日期
+    maxDate: Date,
+  },
+  data() {
+    return {
+      date: null,
+      // 组件可见状态
+      showPicker: false,
+      // 选择的日期
+      pickerValue: [],
+    };
+  },
+  watch: {
+    date(newVal) {
+      // 更新绑定值
+      this.$emit('update:modelValue', newVal);
+      // 传回change事件
+      this.$emit('change', newVal);
+    },
+  },
+  methods: {
+    // 确认触发
+    onConfirm({ selectedValues }) {
+      this.date = selectedValues.join('/');
+      this.pickerValue = selectedValues;
+      this.showPicker = false;
+    },
+  },
+};
+</script>

+ 168 - 0
src/components/VanMultiplePicker/index.vue

@@ -0,0 +1,168 @@
+<template>
+  <div>
+    <van-field v-model="displayText" v-bind="$attrs" readonly is-link :name="name" :label="label" @click="show = !show" :rules="rules" :required="required" rows="1" autosize type="textarea" />
+    <van-popup :show="show" round position="bottom" @click-overlay="show = false, keyWord = null" :style="{ height: '50%' }" class="parent">
+      <div class="control">
+        <div class="button-clear" @click="clear()">清空选择</div>
+        <div class="button-close" @click="show = false, keyWord = null">关闭窗口</div>
+      </div>
+      <van-search v-if="search" v-model="keyWord" placeholder="请输入搜索关键词" class="search" />
+      <div class="checkbox">
+        <van-checkbox-group v-model="checkedValue">
+          <van-cell-group>
+            <van-cell v-for="(item, index) in filteredOptions" :key="item.value" :title="item.text" clickable @click="toggle(item)">
+              <template #right-icon>
+                <van-checkbox :name="item.value" shape="square" />
+              </template>
+            </van-cell>
+          </van-cell-group>
+        </van-checkbox-group>
+      </div>
+    </van-popup>
+  </div>
+</template>
+
+<script>
+import emptyImage from '@/assets/images/custom-empty-image.png';
+
+export default {
+  props: {
+    // 传入组件的复选项原始数组
+    originOptions: {
+      type: Array,
+      required: true,
+      validator: (value) => Array.isArray(value),
+    },
+    // 表单项标签
+    label: String,
+    // 父组件绑定值
+    modelValue: {
+      type: Array,
+      default: () => [],
+    },
+    // 组件name
+    name: String,
+    // 校验规则
+    rules: Array,
+    // 是否显示*标
+    required: Boolean,
+    // 是否开启搜索功能
+    search: {
+      type: Boolean,
+      default: false,
+    },
+  },
+  data() {
+    return {
+      // 组件可见状态
+      show: false,
+      // 空状态图片
+      emptyImage,
+      // 搜索关键词
+      keyWord: '',
+      // 选中的值
+      checkedValue: [],
+    };
+  },
+  computed: {
+    // 计算显示文本
+    displayText() {
+      return this.checkedValue
+        .map((value) => {
+          const option = this.originOptions.find((item) => item.value === value);
+          return option ? option.text : value;
+        })
+        .join('、');
+    },
+    // 过滤后的选项
+    filteredOptions() {
+      if (!this.keyWord || !this.search) {
+        return this.originOptions;
+      }
+
+      return this.originOptions.filter((item) => item.text.includes(this.keyWord));
+    },
+  },
+  watch: {
+    // 监听 modelValue 变化,更新选中状态
+    modelValue: {
+      handler(newVal) {
+        this.updateCheckedValue(newVal);
+      },
+      deep: false,
+      immediate: true,
+    },
+    // 监听 originOptions 变化
+    originOptions(newVal) {
+      // 当选项变化时,重新同步选中状态
+      this.updateCheckedValue(this.checkedValue);
+    },
+  },
+  methods: {
+    // 更新选中值,防止循环更新
+    updateCheckedValue(newValues) {
+      // 过滤出存在于当前选项中的值
+      const validValues = newValues.filter((value) => this.originOptions.some((option) => option.value === value));
+
+      // 只有当值不同时才更新
+      if (validValues.join(',') !== this.checkedValue.join(',')) {
+        this.checkedValue = validValues;
+      }
+    },
+    // 切换选择
+    toggle(item) {
+      const idx = this.checkedValue.indexOf(item.value);
+      if (idx > -1) {
+        // 取消选择
+        this.checkedValue.splice(idx, 1);
+      } else {
+        // 添加选择
+        this.checkedValue.push(item.value);
+      }
+      // 更新绑定值
+      this.$emit('update:modelValue', this.checkedValue);
+      // 传回change事件
+      this.$emit('change', this.checkedValue);
+    },
+    // 清空选择
+    clear() {
+      this.checkedValue = [];
+    }
+  },
+  mounted() {
+    // 初始化选中状态
+    this.updateCheckedValue(this.modelValue);
+  },
+};
+</script>
+
+<style scoped>
+.control {
+  margin-top: 10px;
+}
+
+.button-clear {
+  margin-left: 12px;
+  font-size: 14px;
+  display: inline-block;
+  float: left;
+  color: #1989fa;
+}
+
+.button-close {
+  margin-right: 12px;
+  font-size: 14px;
+  display: inline-block;
+  float: right;
+  color: #1989fa;
+}
+
+.search {
+  width: 100%;
+}
+
+.checkbox {
+  max-height: 80%;
+  overflow: auto;
+}
+</style>

+ 1 - 0
src/components/VanSinglePicker/index.vue

@@ -90,6 +90,7 @@ export default {
     // 监听搜索关键字的变化
     keyWord(newVal) {
       const results = []; 
+      // 筛选下拉选项
       this.columns.forEach((item) => {
         if (item.text.indexOf(newVal) > -1) {
           results.push(item);

+ 20 - 0
src/router/index.js

@@ -25,6 +25,10 @@ import MajorIndex from '@/views/major/index.vue'
 import MajorTodoDetailView from '@/views/major/todoDetail.vue'
 import MajorPlaceOrder from '@/views/major/placeOrder.vue'
 
+// 土规业务
+import LandIndex from '@/views/land/index.vue'
+import LandPlaceOrder from '@/views/land/placeOrder.vue'
+
 const routes = [
   // 重定向到首页的路由
   {
@@ -146,6 +150,22 @@ const routes = [
             },
           },
         ]
+      },
+      // 土规业务
+      {
+        path: 'land',
+        component: LandIndex,
+        name: 'landView',
+        children: [
+          {
+            path: 'placeOrder',
+            component: LandPlaceOrder,
+            name: 'landPlaceOrder',
+            meta: {
+              title: '土规业务下单'
+            },
+          },
+        ]
       }
     ]
   },

+ 1 - 1
src/views/home/start.vue

@@ -36,7 +36,7 @@
         </svg>
         <div class="icon-word">个贷下单</div>
       </div>
-      <div class="icon-area">
+      <div class="icon-area" @click="goBench('/index/land/placeOrder')">
         <svg t="1716189375497" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2709" width="48" height="48">
           <path
             d="M810.666667 1024H213.333333c-117.333333 0-213.333333-96-213.333333-213.333333V213.333333C0 96 96 0 213.333333 0h597.333334c117.333333 0 213.333333 96 213.333333 213.333333v597.333334c0 117.333333-96 213.333333-213.333333 213.333333z"

+ 27 - 0
src/views/land/index.vue

@@ -0,0 +1,27 @@
+<template>
+  <div class="base">
+    <router-view v-slot="{ Component }">
+      <transition>
+        <component :is="Component" />
+      </transition>
+    </router-view>
+  </div>
+</template>
+
+<script>
+
+export default {
+  data() {
+    return {};
+  },
+  created() {
+  },
+  methods: {
+    
+  },
+};
+</script>
+<style scoped>
+.base {
+}
+</style>

+ 237 - 0
src/views/land/placeOrder.vue

@@ -0,0 +1,237 @@
+<template>
+  <div>
+    <van-nav-bar title="土规业务下单" left-text="返回" left-arrow @click-left="onClickLeft()" />
+    <div class="form-style">
+      <van-form @submit="addLandOrder()">
+        <van-cell-group inset>
+          <van-field label="项目名称" v-model="land.name" name="name" type="textarea" placeholder="请输入项目名称" :rules="[{ required: true, message: '请输入项目名称' }]" required />
+          <VanSinglePicker
+            label="归属单位"
+            v-model="land.belongTo"
+            v-bind:columns="belongToAimColumns"
+            name="belongTo"
+            placeholder="请选择归属单位"
+            clearable
+            :rules="[{ required: true, message: '请选择归属单位' }]"
+            :required="true"
+          />
+          <VanSinglePicker
+            label="项目类型"
+            v-model="land.cate"
+            v-bind:columns="cateColumns"
+            name="cate"
+            placeholder="请选择项目类型"
+            clearable
+            :rules="[{ required: true, message: '请选择项目类型' }]"
+            :required="true"
+            :search="true"
+          />
+          <VanSinglePicker label="客户名字" v-model="land.customerId" v-bind:columns="customerColumns" name="customerId" placeholder="请选择客户名字" clearable :search="true" @change="getCustomerDetail()" />
+          <van-field label="委托单位" v-model="land.clientUnit" name="clientUnit" readonly />
+          <van-field label="联系方式" v-model="land.mobile" name="mobile" readonly />
+          <van-field label="客户经理" v-model="land.clientManager" name="clientManager" readonly />
+          <VanSinglePicker
+            label="业务来源"
+            v-model="land.businessSource"
+            v-bind:columns="businessSourceColumns"
+            name="businessSource"
+            placeholder="请选择业务来源"
+            clearable
+            :rules="[{ required: true, message: '请选择业务来源' }]"
+            :required="true"
+            :search="true"
+          />
+          <van-field label="项目负责人" v-model="land.skiller" name="skiller" placeholder="请输入项目负责人" :rules="[{ required: true, message: '请输入项目负责人' }]" required />
+          <VanCalendar label="签订日期" v-model="land.signDate" name="signDate" placeholder="请选择签订日期" clearable />
+          <VanSinglePicker
+            label="付款类型"
+            v-model="land.paymentMethod"
+            v-bind:columns="paymentMethodColumns"
+            name="paymentMethod"
+            placeholder="请选择付款类型"
+            clearable
+            :rules="[{ required: true, message: '请选择付款类型' }]"
+            :required="true"
+          />
+          <van-field label="合同金额" v-model="land.amount" name="amount" type="number" placeholder="请输入合同金额">
+            <template #button>元</template>
+          </van-field>
+          <VanSinglePicker
+            label="所属部门"
+            v-model="land.departmentId"
+            v-bind:columns="departmentColumns"
+            name="departmentId"
+            placeholder="请选择所属部门"
+            clearable
+            :rules="[{ required: true, message: '请选择所属部门' }]"
+            :required="true"
+            :search="true"
+          />
+          <VanMultiplePicker
+            label="人员配置"
+            v-model="land.userIds"
+            name="userIds"
+            placeholder="请选择人员配置"
+            :originOptions="userOptions"
+            clearable
+            :rules="[{ required: true, message: '请选择人员配置' }]"
+            :required="true"
+            :search="true"
+          />
+        </van-cell-group>
+        <div style="margin: 16px;">
+          <van-button round block type="primary" native-type="submit">提交</van-button>
+        </div>
+      </van-form>
+    </div>
+  </div>
+</template>
+<script>
+import { mapStores } from 'pinia';
+import { useUserStore } from '@/stores/useUserStore';
+import { showNotify } from 'vant';
+import VanSinglePicker from '@/components/VanSinglePicker/index.vue';
+import VanCalendar from '@/components/VanCalendar/index.vue';
+import VanMultiplePicker from '@/components/VanMultiplePicker/index.vue';
+import { simpleType } from '@/api/dictData';
+import { simpleAll, detail } from '@/api/customer';
+import { simpleAll as userSimpleAll } from '@/api/user';
+import { simpleAll as depSimpleAll } from '@/api/department';
+import { add } from '@/api/land';
+
+export default {
+  components: {
+    VanSinglePicker,
+    VanCalendar,
+    VanMultiplePicker,
+  },
+  data() {
+    return {
+      // 归属单位
+      belongToAimColumns: [
+        { text: '大友', value: 'DY' },
+        { text: '泰济诚', value: 'TJC' },
+      ],
+      // 项目类型
+      cateColumns: [],
+      // 客户名字
+      customerColumns: [],
+      // 业务来源
+      businessSourceColumns: [],
+      // 付款类型
+      paymentMethodColumns: [
+        { text: '一次性付款', value: '一次性付款' },
+        { text: '分期付款', value: '分期付款' },
+      ],
+      // 所属部门
+      departmentColumns: [],
+      // 用户选项
+      userOptions: [],
+      land: {
+        clientManager: null,
+        name: null,
+        belongTo: null,
+        cate: null,
+        customerId: null,
+        clientUnit: null,
+        mobile: null,
+        businessSource: null,
+        skiller: null,
+        signDate: null,
+        paymentMethod: null,
+        amount: null,
+        paymentMethod: null,
+        userIds: [],
+      },
+    };
+  },
+  computed: {
+    ...mapStores(useUserStore),
+  },
+  created() {
+    this.land.clientManager = this.userStore.userInfo.name;
+    this.getCateColumns();
+    this.getCustomerColumns();
+    this.getBusinessSourceColumns();
+    this.getDepartmentColumns();
+    this.getAllUser();
+  },
+  methods: {
+    onClickLeft() {
+      history.back();
+    },
+    // 获取项目类型
+    getCateColumns() {
+      simpleType('项目类型').then((res) => {
+        if (res.code === 200) {
+          // 使用 map 方法提取 value 和 name 属性
+          this.cateColumns = res.data.map((item) => ({
+            value: String(item.id),
+            text: String(item.name),
+          }));
+        }
+      });
+    },
+    // 获取客户名字
+    getCustomerColumns() {
+      simpleAll().then((res) => {
+        if (res.code === 200) {
+          // 使用 map 方法提取 value 和 name 属性
+          this.customerColumns = res.data.map((item) => ({
+            value: String(item.key),
+            text: String(item.name),
+          }));
+        }
+      });
+    },
+    // 获取客户详情
+    getCustomerDetail() {
+      detail(this.land.customerId).then((res) => {
+        this.land.clientUnit = res.data.department;
+        this.land.mobile = res.data.mobile;
+      });
+    },
+    // 获取业务来源
+    getBusinessSourceColumns() {
+      simpleType('业务来源').then((res) => {
+        if (res.code === 200) {
+          // 使用 map 方法提取 value 和 name 属性
+          this.businessSourceColumns = res.data.map((item) => ({
+            value: String(item.id),
+            text: String(item.name),
+          }));
+        }
+      });
+    },
+    getDepartmentColumns() {
+      depSimpleAll().then((res) => {
+        this.departmentColumns = res.data.map((item) => ({
+          value: String(item.id),
+          text: String(item.name),
+        }));
+      });
+    },
+    // 获取所有用户
+    getAllUser() {
+      userSimpleAll().then((res) => {
+        // 使用 map 方法提取 value 和 name 属性
+        this.userOptions = res.data.map((item) => ({
+          value: String(item.id),
+          text: String(item.name),
+        }));
+      });
+    },
+    // 土规下单
+    addLandOrder() {
+      add(this.land).then((res) => {
+        if (res.code === 200) {
+          showNotify({ type: 'success', message: '新增成功' });
+          history.back();
+        }
+      });
+    },
+  },
+};
+</script>
+<style scoped>
+</style>

+ 6 - 2
src/views/personal/placeOrder.vue

@@ -78,8 +78,12 @@
           <van-field label="中介" v-model="personal.agent" name="agent" placeholder="请输入中介" />
           <van-field label="贷款性质" v-model="personal.loanNature" name="loanNature" placeholder="请输入贷款性质" />
           <van-field label="贷款用途" v-model="personal.loanAim" name="loanAim" placeholder="请输入贷款用途" />
-          <van-field label="贷款额度" v-model="personal.loanLimit" name="loanLimit" type="number" placeholder="请输入贷款额度(万元)" :rules="[{ required: true, message: '请输入贷款额度' }]" required />
-          <van-field label="贷款期限" v-model="personal.loanPeriod" name="loanPeriod" type="number" placeholder="请输入贷款期限(年)" />
+          <van-field label="贷款额度" v-model="personal.loanLimit" name="loanLimit" type="number" placeholder="请输入贷款额度" :rules="[{ required: true, message: '请输入贷款额度' }]" required >
+            <template #button> 万元 </template>
+          </van-field>
+          <van-field label="贷款期限" v-model="personal.loanPeriod" name="loanPeriod" type="number" placeholder="请输入贷款期限" >
+            <template #button> 年 </template>
+          </van-field>
           <van-field name="loanExpire" label="贷款是否到期">
             <template #input>
               <van-radio-group v-model="personal.loanExpire" direction="horizontal" icon-size="14px">