lxf 1 gadu atpakaļ
vecāks
revīzija
aa482373ae

+ 157 - 317
src/components/testForm/index.vue

@@ -7,15 +7,10 @@
       :label-width="formOption.labelWidth"
       :label-align="formOption.labelAlign || 'top'"
       :scroll-to-error="formOption.scroll"
-      ref="testForm"
-    >
+      ref="testForm">
       <van-cell-group inset>
-        <div
-          v-for="(i, index) in formConfig"
-          :key="index"
-          :style="i.style || ''"
-        >
-          <van-field v-if="i.type == 'title'">
+        <div v-for="(i, index) in formConfig" :key="index" :style="i.style || ''">
+          <van-field v-if="i.type == 'title'" style="background: #ecebeb;">
             <template #input>
               <div class="_title">
                 {{ i.title }}
@@ -35,62 +30,27 @@
             :required="getRequired(i.prop)"
             :right-icon="i.isNeedRightBtn ? i.rightIcon : ''"
             @click-right-icon="i.isNeedRightBtn ? i.rightIconClick() : () => {}"
-            @blur="i.isNeedBlurMethon ? i.blurMethon(formData[i.prop]) : () => {}"
-          >
+            @blur="i.isNeedBlurMethon ? i.blurMethon(formData[i.prop]) : () => {}">
           </van-field>
           <!-- switch -->
-          <van-field
-            v-if="i.type == 'switch'"
-            :label="i.label"
-            :name="i.prop"
-            :required="getRequired(i.prop)"
-          >
+          <van-field v-if="i.type == 'switch'" :label="i.label" :name="i.prop" :required="getRequired(i.prop)">
             <template #input>
               <van-switch v-model="formData[i.prop]" />
             </template>
           </van-field>
           <!-- 多选checkbox -->
-          <van-field
-            v-if="i.type == 'checkbox'"
-            :label="i.label"
-            :name="i.prop"
-            :rules="getRules(i.prop)"
-            :required="getRequired(i.prop)"
-          >
+          <van-field v-if="i.type == 'checkbox'" :label="i.label" :name="i.prop" :rules="getRules(i.prop)" :required="getRequired(i.prop)">
             <template #input>
-              <van-checkbox-group
-                v-model="formData[i.prop]"
-                direction="horizontal"
-              >
-                <van-checkbox
-                  shape="square"
-                  v-for="j in i.data"
-                  :key="j.value"
-                  :name="j.value"
-                  >{{ j.text }}</van-checkbox
-                >
+              <van-checkbox-group v-model="formData[i.prop]" direction="horizontal">
+                <van-checkbox shape="square" v-for="j in i.data" :key="j.value" :name="j.value">{{ j.text }}</van-checkbox>
               </van-checkbox-group>
             </template>
           </van-field>
           <!-- 单选radio -->
-          <van-field
-            v-if="i.type == 'radio'"
-            :label="i.label"
-            :name="i.prop"
-            :rules="getRules(i.prop)"
-            :required="getRequired(i.prop)"
-          >
+          <van-field v-if="i.type == 'radio'" :label="i.label" :name="i.prop" :rules="getRules(i.prop)" :required="getRequired(i.prop)">
             <template #input>
-              <van-radio-group
-                v-model="formData[i.prop]"
-                direction="horizontal"
-              >
-                <van-radio
-                  v-for="j in i.data"
-                  :key="j.value"
-                  :name="j.value || j.id"
-                  >{{ j.label || j.title }}</van-radio
-                >
+              <van-radio-group v-model="formData[i.prop]" direction="horizontal">
+                <van-radio v-for="j in i.data" :key="j.value" :name="j.value || j.id">{{ j.label || j.title }}</van-radio>
               </van-radio-group>
             </template>
           </van-field>
@@ -104,58 +64,61 @@
             is-link
             :readonly="true"
             :placeholder="i.placeholder ? i.placeholder : '请选择'"
-            @click="() => (!formOption.readonly ? (i.showPicker = true) : '')"
+            @click="() => (formOption.readonly || i.readonly ? '' : (i.showPicker = true))"
             :rules="getRules(i.prop)"
-            :required="getRequired(i.prop)"
-          >
+            :required="getRequired(i.prop)">
           </van-field>
-          <van-popup
-            v-model:show="i.showPicker"
-            round
-            position="bottom"
-            v-if="i.type == 'picker' && i.itemType == 'onePicker'"
-          >
+          <van-popup v-model:show="i.showPicker" round position="bottom" v-if="i.type == 'picker' && i.itemType == 'onePicker'">
             <van-picker
               :columns="i.data"
-              :columns-field-names="
-                i.fieldNames ? i.fieldNames : onePickerFieldNames
-              "
+              :columns-field-names="i.fieldNames ? i.fieldNames : onePickerFieldNames"
               @cancel="i.showPicker = false"
-              @confirm="
-                (option) =>
-                  i.changeFn
-                    ? i.changeFn(option, i, index)
-                    : onConfirmPicker(option, i, index)
-              "
-            />
+              @confirm="(option) => (i.changeFn ? i.changeFn(option, i, index) : onConfirmPicker(option, i, index))" />
           </van-popup>
-          <!-- 时间选择器 -->
+          <!-- 多选 -->
           <van-field
-            v-if="i.type == 'picker' && i.itemType == 'datePicker'"
+            v-if="i.type == 'multipleChoice' && i.itemType == 'multiple'"
+            v-show="!i.showStatus"
             :label="i.label"
             :name="i.prop"
-            v-model="formData[i.prop]"
+            v-model="formData[i.prop + 'Name']"
             is-link
             :readonly="true"
             :placeholder="i.placeholder ? i.placeholder : '请选择'"
             @click="() => (!formOption.readonly ? (i.showPicker = true) : '')"
             :rules="getRules(i.prop)"
-            :required="getRequired(i.prop)"
-          >
+            :required="getRequired(i.prop)">
           </van-field>
           <van-popup
             v-model:show="i.showPicker"
             round
             position="bottom"
+            :style="{ height: '40%' }"
+            v-if="i.type == 'multipleChoice' && i.itemType == 'multiple'">
+            <van-checkbox-group v-model="formData[i.prop]" @change="changeCheckboxGroup(formData, i.prop, i.data, i.fieldNames)" class="multipleChoice">
+              <van-checkbox v-for="item in i.data" :key="item.value" :name="item.value">{{ item.text }}</van-checkbox>
+            </van-checkbox-group>
+          </van-popup>
+          <!-- 时间选择器 -->
+          <van-field
             v-if="i.type == 'picker' && i.itemType == 'datePicker'"
-          >
+            :label="i.label"
+            :name="i.prop"
+            v-model="formData[i.prop]"
+            is-link
+            :readonly="true"
+            :placeholder="i.placeholder ? i.placeholder : '请选择'"
+            @click="() => (!formOption.readonly ? (i.showPicker = true) : '')"
+            :rules="getRules(i.prop)"
+            :required="getRequired(i.prop)">
+          </van-field>
+          <van-popup v-model:show="i.showPicker" round position="bottom" v-if="i.type == 'picker' && i.itemType == 'datePicker'">
             <van-date-picker
               @confirm="(option) => onConfirmPicker(option, i, index)"
               @cancel="i.showPicker = false"
               :min-date="i.minDate"
               :max-date="i.maxDate"
-              :columns-type="i.columnsType"
-            />
+              :columns-type="i.columnsType" />
           </van-popup>
           <!-- 级联 城市 -->
           <van-field
@@ -168,25 +131,15 @@
             :placeholder="i.placeholder ? i.placeholder : '请选择'"
             @click="() => (!formOption.readonly ? (i.showPicker = true) : '')"
             :rules="getRules(i.prop)"
-            :required="getRequired(i.prop)"
-          />
-          <van-popup
-            v-if="i.type == 'cascader' && i.itemType == 'city'"
-            v-model:show="i.showPicker"
-            round
-            position="bottom"
-          >
+            :required="getRequired(i.prop)" />
+          <van-popup v-if="i.type == 'cascader' && i.itemType == 'city'" v-model:show="i.showPicker" round position="bottom">
             <van-cascader
               v-model="formData[i.prop]"
               :title="i.title ? i.title : '请选择'"
               :options="cityOption"
               @close="i.showPicker = false"
               @change="(option) => cityOnChange(option, i, index)"
-              @finish="
-                (option) =>
-                  i.finishFn ? i.finishFn(option) : () => (i.showPicker = false)
-              "
-            />
+              @finish="(option) => (i.finishFn ? i.finishFn(option) : () => (i.showPicker = false))" />
           </van-popup>
           <!-- 级联 公共 -->
           <van-field
@@ -199,14 +152,8 @@
             :placeholder="i.placeholder ? i.placeholder : '请选择'"
             @click="() => (!formOption.readonly ? (i.showPicker = true) : '')"
             :rules="getRules(i.prop)"
-            :required="getRequired(i.prop)"
-          />
-          <van-popup
-            v-if="i.type == 'cascader' && i.itemType == 'common'"
-            v-model:show="i.showPicker"
-            round
-            position="bottom"
-          >
+            :required="getRequired(i.prop)" />
+          <van-popup v-if="i.type == 'cascader' && i.itemType == 'common'" v-model:show="i.showPicker" round position="bottom">
             <van-cascader
               v-model="formData[i.prop]"
               :title="i.title ? i.title : '请选择'"
@@ -214,37 +161,16 @@
               :field-names="i.fieldNames ? i.fieldNames : fieldNames"
               @close="i.showPicker = false"
               @change="(option) => commonOnChange(option, i, index)"
-              @finish="
-                (option) =>
-                  i.finishFn ? i.finishFn(i, option) : handleCommonFinish(index)
-              "
-            />
+              @finish="(option) => (i.finishFn ? i.finishFn(i, option) : handleCommonFinish(index))" />
           </van-popup>
           <!-- 文件上传 -->
-          <van-field
-            name="uploader"
-            v-if="i.type == 'upload'"
-            :label="i.label"
-            :readonly="i.readonly ? true : false"
-          >
+          <van-field name="uploader" v-if="i.type == 'upload'" :label="i.label" :readonly="i.readonly ? true : false">
             <template #input>
-              <van-uploader
-                v-model="formData[i.prop]"
-                :after-read="afterRead"
-                multiple
-                :max-count="9"
-                :max-size="5 * 1024 * 1024"
-                @oversize="onOversize"
-              />
+              <van-uploader v-model="formData[i.prop]" :after-read="afterRead" multiple :max-count="9" :max-size="5 * 1024 * 1024" @oversize="onOversize" />
             </template>
           </van-field>
           <!-- 插槽 -->
-          <van-field
-            v-if="i.type == 'slot'"
-            :label="i.label"
-            :rules="getRules(i.prop)"
-            :required="getRequired(i.prop)"
-          >
+          <van-field v-if="i.type == 'slot'" :label="i.label" :rules="getRules(i.prop)" :required="getRequired(i.prop)">
             <template #input>
               <div style="width: 100%">
                 <slot :name="i.slotName"> {{ i.slotName }}插槽占位符 </slot>
@@ -254,11 +180,7 @@
         </div>
       </van-cell-group>
       <!-- 循环业务数据 -->
-      <van-cell-group
-        inset
-        v-for="(item, index) in formData[btnConfigCopy.prop]"
-        :key="index"
-      >
+      <van-cell-group inset v-for="(item, index) in formData[btnConfigCopy.prop]" :key="index">
         <div class="row">
           <div>{{ btnConfigCopy.listTitle || "明细" }}{{ index + 1 }}</div>
           <van-button
@@ -267,9 +189,7 @@
             @click="handleRemove(index, item)"
             size="mini"
             style="border: none; background: #ecebeb"
-            v-if="
-              formOption.btnConfig !== undefined && formOption.btnConfig.isNeed
-            "
+            v-if="formOption.btnConfig !== undefined && formOption.btnConfig.isNeed"
             >删除</van-button
           >
         </div>
@@ -290,8 +210,7 @@
               (val) => {
                 return i.changeFn ? i.changeFn(index, val) : () => {};
               }
-            "
-          >
+            ">
           </van-field>
           <!-- 单选 -->
           <van-field
@@ -304,22 +223,14 @@
             :placeholder="i.placeholder ? i.placeholder : '请选择'"
             @click="handleListItemClick(i, index, sonIndex)"
             :rules="getRules(i.prop)"
-            :required="getRequired(i.prop)"
-          >
+            :required="getRequired(i.prop)">
             <template #input v-if="i.isShowScanCode">
               <div style="display: flex; height: 24px">
                 <div style="width: calc(100vw - 100px)">
                   {{ formData[btnConfigCopy.prop][index][i.prop + "Name"] }}
                 </div>
                 <div style="width: 100px; float: right; margin-top: -20px">
-                  <van-button
-                    plain
-                    type="primary"
-                    @click.native.stop="i.scanCode(index)"
-                    size="mini"
-                    style="border: none"
-                    >扫码</van-button
-                  >
+                  <van-button plain type="primary" @click.native.stop="i.scanCode(index)" size="mini" style="border: none">扫码</van-button>
                 </div>
               </div>
             </template>
@@ -335,65 +246,41 @@
             :placeholder="i.placeholder ? i.placeholder : '请选择'"
             @click="handleListItemClick(i, index, sonIndex)"
             :rules="getRules(i.prop)"
-            :required="getRequired(i.prop)"
-          >
+            :required="getRequired(i.prop)">
           </van-field>
         </div>
       </van-cell-group>
       <!-- 单独写个循环,保证弹窗唯一 -->
       <div v-for="(item, index) in btnConfigCopy.listConfig" :key="index">
-        <van-popup
-          v-model:show="item.showPicker"
-          round
-          position="bottom"
-          v-if="item.type == 'picker' && item.itemType == 'onePicker'"
-        >
+        <van-popup v-model:show="item.showPicker" round position="bottom" v-if="item.type == 'picker' && item.itemType == 'onePicker'">
           <van-picker
             :columns="item.data"
-            :columns-field-names="
-              item.fieldNames ? item.fieldNames : onePickerFieldNames
-            "
+            :columns-field-names="item.fieldNames ? item.fieldNames : onePickerFieldNames"
             @cancel="item.showPicker = false"
-            @confirm="(option) => onConfirmListPicker(option, item)"
-          />
+            @confirm="(option) => onConfirmListPicker(option, item)" />
         </van-popup>
-        <van-popup
-          v-model:show="item.showPicker"
-          round
-          position="bottom"
-          v-if="item.type == 'picker' && item.itemType == 'datePicker'"
-        >
+        <van-popup v-model:show="item.showPicker" round position="bottom" v-if="item.type == 'picker' && item.itemType == 'datePicker'">
           <van-date-picker
             @confirm="(option) => onConfirmListPicker(option, item)"
             @cancel="item.showPicker = false"
             :min-date="item.minDate"
             :max-date="item.maxDate"
-            :columns-type="item.columnsType"
-          />
+            :columns-type="item.columnsType" />
         </van-popup>
       </div>
 
       <!-- 按钮 -->
-      <div
-        class="btn-box"
-        v-if="formOption.btnConfig !== undefined && formOption.btnConfig.isNeed"
-      >
+      <div class="btn-box" v-if="formOption.btnConfig !== undefined && formOption.btnConfig.isNeed">
         <van-button
           :plain="btnConfigCopy.plain ? btnConfigCopy.plain : false"
           :type="btnConfigCopy.itemType ? btnConfigCopy.itemType : 'primary'"
           :size="btnConfigCopy.size ? btnConfigCopy.size : 'small'"
           style="width: 100%; border: none"
-          @click="handlePush()"
-        >
+          @click="handlePush()">
           <template #icon>
-            <van-icon
-              :name="btnConfigCopy.icon ? btnConfigCopy.icon : 'plus'"
-              :size="12"
-            />
+            <van-icon :name="btnConfigCopy.icon ? btnConfigCopy.icon : 'plus'" :size="12" />
           </template>
-          {{
-            btnConfigCopy.btnName ? btnConfigCopy.btnName : "添加"
-          }}</van-button
+          {{ btnConfigCopy.btnName ? btnConfigCopy.btnName : "添加" }}</van-button
         >
       </div>
       <div style="margin: 16px" v-show="!formOption.hiddenSubmitBtn">
@@ -412,15 +299,7 @@
 
 <script setup>
 import { showLoadingToast, closeToast, showNotify } from "vant";
-import {
-  ref,
-  getCurrentInstance,
-  onMounted,
-  reactive,
-  computed,
-  toRefs,
-  watch,
-} from "vue";
+import { ref, getCurrentInstance, onMounted, reactive, computed, toRefs, watch } from "vue";
 
 const props = defineProps({
   modelValue: {
@@ -520,20 +399,14 @@ const recursionFn = (arr, val, valueAtt, childrenAtt) => {
 };
 const selectDataEcho = (item, val) => {
   if (item.type === "picker" && item.itemType === "onePicker") {
-    const textAtt = item.fieldNames
-      ? item.fieldNames.text
-      : onePickerFieldNames.text;
-    const valueAtt = item.fieldNames
-      ? item.fieldNames.value
-      : onePickerFieldNames.value;
+    const textAtt = item.fieldNames ? item.fieldNames.text : onePickerFieldNames.text;
+    const valueAtt = item.fieldNames ? item.fieldNames.value : onePickerFieldNames.value;
     const current = item.data.find((x) => x[valueAtt] === val);
     return current ? current[textAtt] : "";
   } else if (item.type === "cascader" && item.itemType === "common") {
     const textAtt = item.fieldNames ? item.fieldNames.text : fieldNames.text;
     const valueAtt = item.fieldNames ? item.fieldNames.value : fieldNames.value;
-    const childrenAtt = item.fieldNames
-      ? item.fieldNames.children
-      : fieldNames.children;
+    const childrenAtt = item.fieldNames ? item.fieldNames.children : fieldNames.children;
     const arr = item.data ? item.data : [];
     const current = recursionFn(arr, val, valueAtt, childrenAtt);
     return current ? current[textAtt] : "";
@@ -550,11 +423,7 @@ const formDataListShowLabel = () => {
       const jele = btnConfigCopy.listConfig[j];
       if (jele.type === "picker" && jele.itemType !== "datePicker") {
         if (jele.data && jele.data.length > 0) {
-          formData.value[btnConfigCopy.prop][i][jele.prop + "Name"] =
-            selectDataEcho(
-              jele,
-              formData.value[btnConfigCopy.prop][i][jele.prop]
-            );
+          formData.value[btnConfigCopy.prop][i][jele.prop + "Name"] = selectDataEcho(jele, formData.value[btnConfigCopy.prop][i][jele.prop]);
         } else {
           if (callListNum.value <= 3) {
             setTimeout(() => {
@@ -574,10 +443,7 @@ const formDataShowLabel = () => {
     const element = formConfig.value[i];
     if (element.type === "picker" && element.itemType !== "datePicker") {
       if (element.data && element.data.length > 0) {
-        formData.value[element.prop + "Name"] = selectDataEcho(
-          element,
-          formData.value[element.prop]
-        );
+        formData.value[element.prop + "Name"] = selectDataEcho(element, formData.value[element.prop]);
       } else {
         if (callNum.value <= 3) {
           setTimeout(() => {
@@ -589,10 +455,7 @@ const formDataShowLabel = () => {
       }
     } else if (element.type === "cascader" && element.itemType === "common") {
       if (element.data && element.data.length > 0) {
-        formData.value[element.prop + "Name"] = selectDataEcho(
-          element,
-          formData.value[element.prop]
-        );
+        formData.value[element.prop + "Name"] = selectDataEcho(element, formData.value[element.prop]);
       } else {
         if (callNum.value <= 3) {
           setTimeout(() => {
@@ -618,10 +481,7 @@ const formDataInit = () => {
     upload: [],
   };
   // 判断是否需要按钮
-  if (
-    formOption.value.btnConfig &&
-    Object.keys(formOption.value.btnConfig).length > 0
-  ) {
+  if (formOption.value.btnConfig && Object.keys(formOption.value.btnConfig).length > 0) {
     btnConfigCopy = { ...formOption.value.btnConfig };
     if (formData.value[btnConfigCopy.prop] === undefined) {
       formData.value[btnConfigCopy.prop] = [];
@@ -635,10 +495,7 @@ const formDataInit = () => {
       // formData.value[element.prop] = map[element.type];
       // formData.value[element.prop + "Name"] = map[element.type];
     }
-    if (
-      formData.value[element.prop] === undefined ||
-      formData.value[element.prop] === ""
-    ) {
+    if (formData.value[element.prop] === undefined || formData.value[element.prop] === "") {
       if (element.type === "slot") {
         continue;
       } else if (element.type === "picker" || element.type === "cascader") {
@@ -662,24 +519,12 @@ const onConfirmPicker = (option, item, index) => {
   if (option.selectedOptions[0]) {
     switch (item.itemType) {
       case "onePicker": {
-        formData.value[item.prop + "Name"] =
-          option.selectedOptions[0][
-            item.fieldNames.text
-              ? item.fieldNames.text
-              : onePickerFieldNames.text
-          ];
-        formData.value[item.prop] =
-          option.selectedOptions[0][
-            item.fieldNames.value
-              ? item.fieldNames.value
-              : onePickerFieldNames.value
-          ];
+        formData.value[item.prop + "Name"] = option.selectedOptions[0][item.fieldNames.text ? item.fieldNames.text : onePickerFieldNames.text];
+        formData.value[item.prop] = option.selectedOptions[0][item.fieldNames.value ? item.fieldNames.value : onePickerFieldNames.value];
         formConfig.value[index].showPicker = false;
       }
       case "datePicker": {
-        formData.value[item.prop] = option.selectedValues.join(
-          item.split ? item.split : "-"
-        );
+        formData.value[item.prop] = option.selectedValues.join(item.split ? item.split : "-");
         formConfig.value[index].showPicker = false;
       }
     }
@@ -705,23 +550,14 @@ const handleListItemClick = (i, index, sonIndex) => {
 const onConfirmListPicker = (option, item) => {
   switch (item.itemType) {
     case "onePicker": {
-      formData.value[btnConfigCopy.prop][currentIndex.value][
-        item.prop + "Name"
-      ] =
-        option.selectedOptions[0][
-          item.fieldNames.text ? item.fieldNames.text : onePickerFieldNames.text
-        ];
+      formData.value[btnConfigCopy.prop][currentIndex.value][item.prop + "Name"] =
+        option.selectedOptions[0][item.fieldNames.text ? item.fieldNames.text : onePickerFieldNames.text];
       formData.value[btnConfigCopy.prop][currentIndex.value][item.prop] =
-        option.selectedOptions[0][
-          item.fieldNames.value
-            ? item.fieldNames.value
-            : onePickerFieldNames.value
-        ];
+        option.selectedOptions[0][item.fieldNames.value ? item.fieldNames.value : onePickerFieldNames.value];
       btnConfigCopy.listConfig[currentSonIndex.value].showPicker = false;
     }
     case "datePicker": {
-      formData.value[btnConfigCopy.prop][currentIndex.value][item.prop] =
-        option.selectedValues.join(item.split ? item.split : "-");
+      formData.value[btnConfigCopy.prop][currentIndex.value][item.prop] = option.selectedValues.join(item.split ? item.split : "-");
       btnConfigCopy.listConfig[currentSonIndex.value].showPicker = false;
     }
   }
@@ -745,72 +581,55 @@ const handleRemove = (index, item) => {
 // 拉去城市最近数据及处理
 const getAreaInfo = (selectedOptions, item, index) => {
   showLoadingToast("加载中...");
-  proxy
-    .post("/customizeArea/list", { parentId: selectedOptions.value })
-    .then((res) => {
-      let countryIndex = selectedOptions.selectedOptions[0].index;
-      let provinceIndex =
-        selectedOptions.tabIndex === 1
-          ? selectedOptions.selectedOptions[1].index
-          : null;
-      let cityIndex =
-        selectedOptions.tabIndex === 2
-          ? selectedOptions.selectedOptions[2].index
-          : null;
-      //已经没有下级数据
-      if (res.data.length === 0) {
-        if (selectedOptions.tabIndex === 1) {
-          formData.value[item.prop + "Name"] = selectedOptions.selectedOptions
-            .map((item) => item.text)
-            .join(" ");
-          formConfig.value[index].showPicker = false;
-          formData.value.cityObj = selectedOptions;
-          return;
-        }
-      }
-      if (selectedOptions.tabIndex === 2) {
-        formData.value[item.prop + "Name"] = selectedOptions.selectedOptions
-          .map((item) => item.text)
-          .join(" ");
+  proxy.post("/customizeArea/list", { parentId: selectedOptions.value }).then((res) => {
+    let countryIndex = selectedOptions.selectedOptions[0].index;
+    let provinceIndex = selectedOptions.tabIndex === 1 ? selectedOptions.selectedOptions[1].index : null;
+    let cityIndex = selectedOptions.tabIndex === 2 ? selectedOptions.selectedOptions[2].index : null;
+    //已经没有下级数据
+    if (res.data.length === 0) {
+      if (selectedOptions.tabIndex === 1) {
+        formData.value[item.prop + "Name"] = selectedOptions.selectedOptions.map((item) => item.text).join(" ");
         formConfig.value[index].showPicker = false;
         formData.value.cityObj = selectedOptions;
         return;
       }
-      if (selectedOptions.tabIndex === 0) {
-        cityOption.value[countryIndex].children = res.data.map(
-          (item, index) => {
-            return {
-              ...item,
-              index: index,
-              text: item.name,
-              value: item.id,
-            };
-          }
-        );
-      } else if (selectedOptions.tabIndex === 1) {
-        cityOption.value[countryIndex].children[provinceIndex].children =
-          res.data.map((item, index) => {
-            return {
-              ...item,
-              index: index,
-              text: item.name,
-              value: item.id,
-            };
-          });
-      } else if (selectedOptions.tabIndex === 2) {
-        cityOption.value[countryIndex].children[provinceIndex].children[
-          cityIndex
-        ].children = res.data.map((item, index) => {
-          return {
-            ...item,
-            index: index,
-            text: item.name,
-            value: item.id,
-          };
-        });
-      }
-      closeToast();
-    });
+    }
+    if (selectedOptions.tabIndex === 2) {
+      formData.value[item.prop + "Name"] = selectedOptions.selectedOptions.map((item) => item.text).join(" ");
+      formConfig.value[index].showPicker = false;
+      formData.value.cityObj = selectedOptions;
+      return;
+    }
+    if (selectedOptions.tabIndex === 0) {
+      cityOption.value[countryIndex].children = res.data.map((item, index) => {
+        return {
+          ...item,
+          index: index,
+          text: item.name,
+          value: item.id,
+        };
+      });
+    } else if (selectedOptions.tabIndex === 1) {
+      cityOption.value[countryIndex].children[provinceIndex].children = res.data.map((item, index) => {
+        return {
+          ...item,
+          index: index,
+          text: item.name,
+          value: item.id,
+        };
+      });
+    } else if (selectedOptions.tabIndex === 2) {
+      cityOption.value[countryIndex].children[provinceIndex].children[cityIndex].children = res.data.map((item, index) => {
+        return {
+          ...item,
+          index: index,
+          text: item.name,
+          value: item.id,
+        };
+      });
+    }
+    closeToast();
+  });
 };
 // 城市变动事件
 const cityOnChange = (options, item, index) => {
@@ -819,8 +638,7 @@ const cityOnChange = (options, item, index) => {
 
 const commonOnChange = ({ selectedOptions }, item, index) => {
   const textAtt = item.fieldNames ? item.fieldNames.text : fieldNames.text;
-  formData.value[item.prop + "Name"] =
-    selectedOptions[selectedOptions.length - 1][textAtt];
+  formData.value[item.prop + "Name"] = selectedOptions[selectedOptions.length - 1][textAtt];
 };
 const handleCommonFinish = (index) => {
   formConfig.value[index].showPicker = false;
@@ -911,17 +729,27 @@ watch(
 watch(
   () => formData.value[btnConfigCopy.prop],
   (val) => {
-    if (
-      formOption.value.btnConfig !== undefined &&
-      !formOption.value.btnConfig.isNeed &&
-      val &&
-      val.length > 0
-    ) {
+    if (formOption.value.btnConfig !== undefined && !formOption.value.btnConfig.isNeed && val && val.length > 0) {
       formDataListShowLabel();
     }
   }
 );
-
+const changeCheckboxGroup = (form, label, data, fieldNames) => {
+  let text = "";
+  if (form[label] && form[label].length > 0) {
+    form[label].map((item) => {
+      let list = data.filter((itemData) => itemData[fieldNames.value] === item);
+      if (list && list.length > 0) {
+        if (text) {
+          text = text + "," + list[0][fieldNames.text];
+        } else {
+          text = list[0][fieldNames.text];
+        }
+      }
+    });
+  }
+  form[label + "Name"] = text;
+};
 // onMounted(() => {});
 </script>
 
@@ -945,4 +773,16 @@ watch(
   font-size: 14px;
   font-weight: 700;
 }
-</style>
+::v-deep {
+  .multipleChoice {
+    margin-bottom: 60px;
+    .van-checkbox {
+      justify-content: center;
+      margin: 10px 40px;
+    }
+  }
+  .van-form {
+    margin-top: 0px !important; 
+  }
+}
+</style>

+ 94 - 0
src/lang/cn.js

@@ -1109,5 +1109,99 @@ export const lang = {
 		bankAccountNumber: '银行账号',
 		otherInformation: '其他信息',
 		remark: '备注',
+	},
+	account: {
+		name: '资金账户',
+		company: '归属公司',
+		companyMsg: '请选择归属公司',
+		add: '添加账户',
+		edit: '编辑账户',
+		alias: '账户别名',
+		aliasMsg: '请输入账户别名',
+		openingBank: '开户银行',
+		openingBankMsg: '请输入开户银行',
+		accountName: '账户名',
+		accountNameMsg: '请输入账户名',
+		accountOpening: '银行账号',
+		accountOpeningMsg: '请输入银行账号',
+		interbankNumber: '联行号',
+		foreignExchange: '外汇信息',
+		beneficiaryName: 'Beneficiary Name',
+		beneficiaryBank: 'Beneficiary Bank',
+		beneficiaryBankAddress: 'Beneficiary Bank Address',
+		beneficiaryAccountNumber: 'Beneficiary Account Number',
+		swiftCode: 'Swift Code',
+		beneficiaryAddress: 'Beneficiary Address',
+		currency: '币种',
+		currencyMsg: '请选择币种',
+		remainder: '余额',
+		remainderMsg: '请输入余额',
+		accountBalance: '账户余额',
+		submitMsgOne:'请添加至少一条账户余额',
+		submitMsgTwo:'请勿重复添加货币余额',
+	},
+	customerFile: {
+		name: '客户档案',
+		customerName: '客户名称',
+		customerNameMsg: '请输入名称',
+		cityText: '所在城市',
+		customerStatus: '客户类型',
+		add: '添加客户',
+		address: '详细地址',
+		code: '客户代码',
+		source: '客户来源',
+		sourceMsg: '请选择客户来源',
+		status: '客户类型',
+		statusMsg: '请选择客户类型',
+		userId: '业务员',
+		tag: '客户标签',
+		cityMsg: '请选择城市',
+		emailMsg: '请输入邮箱',
+		submitMsg: '请添加联系人',
+		customerContact: '客户联系人',
+		contact: '联系人',
+		email: '电子邮箱',
+		detail: '客户详情',
+		followUpRecord: '添加跟进记录',
+		date: '跟进时间',
+		dateMsg: '请选择跟进时间',
+		customerName: '跟进人',
+		content: '跟进记录',
+		contentTwo: '跟进内容',
+		contentTwoMsg: '请输入跟进内容',
+		highseasName: '公海客户',
+		privateseaName: '私海客户',
+	},
+	accountPayment: {
+		name: '打款',
+		corporationName: '归属公司',
+		createTime: '请款时间',
+		currencyAmount: '请款金额',
+		paymentRemark: '请款说明',
+		statusText: '打款状态',
+		requestInformation: '请款信息',
+		businessManagementName: '付款账户',
+		paymentMethodText: '付款方式',
+		collectionName: '收款户名',
+		accountOpening: '银行账号',
+		openingBank: '开户银行',
+		interbankNumber: '联行号',
+		paymentInformation: '打款信息',
+		accountManagementId: '打款账户',
+		accountManagementIdMsg: '请选择打款账户',
+		currency: '打款币种',
+		currencyMsg: '请选择打款币种',
+		amount: '打款金额',
+		amountMsg: '请选择打款金额',
+		expensesTime: '打款时间',
+		expensesTimeMsg: '请选择打款时间',
+		remark: '摘要',
+	},
+	funds: {
+		name: '请款',
+		createTime: '请款时间',
+		currencyTotal: '请款金额',
+		paymentRemarks: '用款说明',
+		fundsText: '放款状态',
 	}
 }

+ 60 - 0
src/router/routerLXF.js

@@ -75,6 +75,66 @@ export function routesLXF() {
       name: "添加流水",
       component: () => import("../views/fund/flow-of-funds/add.vue"),
     },
+    {
+      path: "account",
+      name: "资金账户",
+      component: () => import("../views/fund/account/index.vue"),
+    },
+    {
+      path: "accountAdd",
+      name: "添加账户",
+      component: () => import("../views/fund/account/add.vue"),
+    },
+    {
+      path: "customerFile",
+      name: "客户档案",
+      component: () => import("../views/customer/file/index.vue"),
+    },
+    {
+      path: "customerFileAdd",
+      name: "添加客户",
+      component: () => import("../views/customer/file/add.vue"),
+    },
+    {
+      path: "customerFileDetail",
+      name: "客户详情",
+      component: () => import("../views/customer/file/detail.vue"),
+    },
+    {
+      path: "customerFileRecords",
+      name: "添加跟进记录",
+      component: () => import("../views/customer/file/addRecords.vue"),
+    },
+    {
+      path: "highseas",
+      name: "公海客户",
+      component: () => import("../views/customer/highseas/index.vue"),
+    },
+    {
+      path: "privatesea",
+      name: "私海客户",
+      component: () => import("../views/customer/privatesea/index.vue"),
+    },
+    {
+      path: "accountPayment",
+      name: "打款",
+      component: () => import("../views/fund/account-payment/index.vue"),
+    },
+    {
+      path: "accountPaymentAdd",
+      name: "添加打款",
+      component: () => import("../views/fund/account-payment/add.vue"),
+    },
+    {
+      path: "accountPaymentDetail",
+      name: "打款详情",
+      component: () => import("../views/fund/account-payment/detail.vue"),
+    },
+    {
+      path: "funds",
+      name: "请款",
+      component: () => import("../views/fund/funds/index.vue"),
+    },
   ];
   return routesLXF;
 }

+ 98 - 0
src/views/customer/file/add.vue

@@ -0,0 +1,98 @@
+<template>
+  <div class="form">
+    <van-nav-bar :title="$t('customerFile.followUpRecord')" :left-text="$t('common.back')" left-arrow @click-left="onClickLeft"> </van-nav-bar>
+    <testForm v-model="formData.data" :formOption="formOption" :formConfig="formConfig" :rules="rules" @onSubmit="onSubmit" ref="formDom">
+      <template #date>
+        <div style="width: 100%">
+          <van-cell-group inset>
+            <van-field
+              v-model="formData.data.date"
+              is-link
+              readonly
+              :label="$t('customerFile.date')"
+              :placeholder="$t('common.pleaseSelect')"
+              style="padding: 0 !important"
+              :required="true"
+              @click="clickDate" />
+            <van-popup v-model:show="showPicker" round position="bottom">
+              <van-picker-group
+                :title="$t('customerFile.date')"
+                :tabs="[$t('common.selectDate'), $t('common.selectTime')]"
+                @confirm="onConfirm"
+                @cancel="onCancel">
+                <van-date-picker v-model="currentDate" />
+                <van-time-picker v-model="currentTime" :columns-type="columnsType" />
+              </van-picker-group>
+            </van-popup>
+          </van-cell-group>
+        </div>
+      </template>
+    </testForm>
+  </div>
+</template>
+
+<script setup>
+import { ref, getCurrentInstance, reactive } from "vue";
+import { showSuccessToast } from "vant";
+import testForm from "@/components/testForm/index.vue";
+import { formatDate } from "@/utils/auth";
+import { useRoute } from "vue-router";
+
+const proxy = getCurrentInstance().proxy;
+const onClickLeft = () => history.back();
+const route = useRoute();
+const currentDate = ref([]);
+const currentTime = ref([]);
+const columnsType = ["hour", "minute", "second"];
+const onConfirm = () => {
+  formData.data.date = currentDate.value.join("-") + " " + currentTime.value.join(":");
+  showPicker.value = false;
+};
+const onCancel = () => {
+  showPicker.value = false;
+};
+const clickDate = () => {
+  currentDate.value = formatDate(new Date(formData.data.date), "yyyy-MM-dd").split("-");
+  currentTime.value = formatDate(new Date(formData.data.date), "hh:mm:ss").split(":");
+  showPicker.value = true;
+};
+const formData = reactive({
+  data: {
+    date: formatDate(new Date(), "yyyy-MM-dd hh:mm:ss"),
+    content: null,
+  },
+});
+const formDom = ref(null);
+const showPicker = ref(false);
+const formOption = reactive({
+  readonly: false, //用于控制整个表单是否只读
+  disabled: false,
+  labelAlign: "top",
+  scroll: true,
+  labelWidth: "62pk",
+});
+const formConfig = reactive([
+  {
+    type: "slot",
+    slotName: "date",
+  },
+  {
+    type: "input",
+    label: proxy.t("customerFile.contentTwo"),
+    prop: "content",
+    itemType: "textarea",
+  },
+]);
+const rules = {
+  date: [{ required: true, message: proxy.t("customerFile.dateMsg") }],
+  content: [{ required: true, message: proxy.t("customerFile.contentTwoMsg") }],
+};
+const onSubmit = () => {
+  proxy.post("/customerFollowRecords/add", { ...formData.data, customerId: route.query.id }).then(() => {
+    showSuccessToast(proxy.t("common.addSuccess"));
+    setTimeout(() => {
+      history.back();
+    }, 500);
+  });
+};
+</script>

+ 98 - 0
src/views/customer/file/addRecords.vue

@@ -0,0 +1,98 @@
+<template>
+  <div class="form">
+    <van-nav-bar :title="$t('customerFile.followUpRecord')" :left-text="$t('common.back')" left-arrow @click-left="onClickLeft"> </van-nav-bar>
+    <testForm v-model="formData.data" :formOption="formOption" :formConfig="formConfig" :rules="rules" @onSubmit="onSubmit" ref="formDom">
+      <template #date>
+        <div style="width: 100%">
+          <van-cell-group inset>
+            <van-field
+              v-model="formData.data.date"
+              is-link
+              readonly
+              :label="$t('customerFile.date')"
+              :placeholder="$t('common.pleaseSelect')"
+              style="padding: 0 !important"
+              :required="true"
+              @click="clickDate" />
+            <van-popup v-model:show="showPicker" round position="bottom">
+              <van-picker-group
+                :title="$t('customerFile.date')"
+                :tabs="[$t('customerFile.selectDate'), $t('customerFile.selectTime')]"
+                @confirm="onConfirm"
+                @cancel="onCancel">
+                <van-date-picker v-model="currentDate" />
+                <van-time-picker v-model="currentTime" :columns-type="columnsType" />
+              </van-picker-group>
+            </van-popup>
+          </van-cell-group>
+        </div>
+      </template>
+    </testForm>
+  </div>
+</template>
+
+<script setup>
+import { ref, getCurrentInstance, reactive } from "vue";
+import { showSuccessToast } from "vant";
+import testForm from "@/components/testForm/index.vue";
+import { formatDate } from "@/utils/auth";
+import { useRoute } from "vue-router";
+
+const proxy = getCurrentInstance().proxy;
+const onClickLeft = () => history.back();
+const route = useRoute();
+const currentDate = ref([]);
+const currentTime = ref([]);
+const columnsType = ["hour", "minute", "second"];
+const onConfirm = () => {
+  formData.data.date = currentDate.value.join("-") + " " + currentTime.value.join(":");
+  showPicker.value = false;
+};
+const onCancel = () => {
+  showPicker.value = false;
+};
+const clickDate = () => {
+  currentDate.value = formatDate(new Date(formData.data.date), "yyyy-MM-dd").split("-");
+  currentTime.value = formatDate(new Date(formData.data.date), "hh:mm:ss").split(":");
+  showPicker.value = true;
+};
+const formData = reactive({
+  data: {
+    date: formatDate(new Date(), "yyyy-MM-dd hh:mm:ss"),
+    content: null,
+  },
+});
+const formDom = ref(null);
+const showPicker = ref(false);
+const formOption = reactive({
+  readonly: false, //用于控制整个表单是否只读
+  disabled: false,
+  labelAlign: "top",
+  scroll: true,
+  labelWidth: "62pk",
+});
+const formConfig = reactive([
+  {
+    type: "slot",
+    slotName: "date",
+  },
+  {
+    type: "input",
+    label: proxy.t("customerFile.contentTwo"),
+    prop: "content",
+    itemType: "textarea",
+  },
+]);
+const rules = {
+  date: [{ required: true, message: proxy.t("customerFile.dateMsg") }],
+  content: [{ required: true, message: proxy.t("customerFile.contentTwoMsg") }],
+};
+const onSubmit = () => {
+  proxy.post("/customerFollowRecords/add", { ...formData.data, customerId: route.query.id }).then(() => {
+    showSuccessToast(proxy.t("common.addSuccess"));
+    setTimeout(() => {
+      history.back();
+    }, 500);
+  });
+};
+</script>

+ 267 - 0
src/views/customer/file/detail.vue

@@ -0,0 +1,267 @@
+<template>
+  <div class="form">
+    <van-nav-bar :title="$t('customerFile.' + route.query.type)" :left-text="$t('common.back')" left-arrow @click-left="onClickLeft"> </van-nav-bar>
+    <testForm v-model="formData.data" :formOption="formOption" :formConfig="formConfig"> </testForm>
+    <testForm v-model="formData.data" :formOption="formOptionTwo" :formConfig="formConfigTwo" @onSubmit="onSubmit"> </testForm>
+  </div>
+</template>
+
+<script setup>
+import { ref, getCurrentInstance, onMounted, reactive } from "vue";
+import { useRoute } from "vue-router";
+import { getUserInfo } from "@/utils/auth";
+import testForm from "@/components/testForm/index.vue";
+
+const proxy = getCurrentInstance().proxy;
+const onClickLeft = () => history.back();
+const route = useRoute();
+const customerSource = ref([]);
+const customerStatus = ref([]);
+const customerTag = ref([]);
+const userList = ref([]);
+const formData = reactive({
+  data: {
+    customerCode: null,
+    code: null,
+    countryId: null,
+    provinceId: null,
+    cityId: null,
+    address: null,
+    zipCode: null,
+    name: null,
+    status: null,
+    source: null,
+    userId: null,
+    customerUserList: [],
+  },
+});
+const formOption = reactive({
+  readonly: true, //用于控制整个表单是否只读
+  disabled: false,
+  labelAlign: "top",
+  scroll: true,
+  labelWidth: "62pk",
+  hiddenSubmitBtn: true,
+  btnConfig: {
+    isNeed: false,
+    prop: "customerUserList",
+    plain: true,
+    listTitle: proxy.t("customerFile.customerContact"),
+    listConfig: [
+      {
+        type: "input",
+        label: proxy.t("customerFile.contact"),
+        prop: "name",
+      },
+      {
+        type: "input",
+        label: proxy.t("customerFile.email"),
+        prop: "email",
+      },
+    ],
+  },
+});
+const formConfig = reactive([
+  {
+    type: "input",
+    label: proxy.t("customerFile.customerName"),
+    prop: "name",
+    itemType: "text",
+  },
+  {
+    type: "input",
+    label: proxy.t("customerFile.cityText"),
+    prop: "cityText",
+    itemType: "text",
+  },
+  {
+    type: "input",
+    label: proxy.t("customerFile.address"),
+    prop: "address",
+    itemType: "textarea",
+  },
+  {
+    type: "input",
+    label: proxy.t("customerFile.code"),
+    prop: "code",
+    itemType: "text",
+  },
+  {
+    type: "input",
+    label: proxy.t("customerFile.source"),
+    prop: "sourceText",
+    itemType: "text",
+  },
+  {
+    type: "input",
+    label: proxy.t("customerFile.status"),
+    prop: "statusText",
+    itemType: "text",
+  },
+  {
+    type: "input",
+    label: proxy.t("customerFile.userId"),
+    prop: "userText",
+    itemType: "text",
+  },
+  {
+    type: "input",
+    label: proxy.t("customerFile.tag"),
+    prop: "tagText",
+    itemType: "text",
+  },
+]);
+const formOptionTwo = reactive({
+  readonly: true, //用于控制整个表单是否只读
+  disabled: false,
+  labelAlign: "top",
+  scroll: true,
+  labelWidth: "62pk",
+  hiddenSubmitBtn: false,
+  submitBtnText: proxy.t("customerFile.followUpRecord"),
+  btnConfig: {
+    isNeed: false,
+    prop: "recordsList",
+    plain: true,
+    listTitle: proxy.t("customerFile.content"),
+    listConfig: [
+      {
+        type: "input",
+        label: proxy.t("customerFile.date"),
+        prop: "date",
+      },
+      {
+        type: "input",
+        label: proxy.t("customerFile.customerName"),
+        prop: "customerName",
+      },
+      {
+        type: "input",
+        label: proxy.t("customerFile.content"),
+        prop: "content",
+      },
+    ],
+  },
+});
+const formConfigTwo = reactive([]);
+const onSubmit = () => {
+  proxy.$router.push({
+    path: "customerFileRecords",
+    query: {
+      id: route.query.id,
+    },
+  });
+};
+const getUser = () => {
+  return proxy.get("/tenantUser/list", { pageNum: 1, pageSize: 10000, tenantId: getUserInfo().tenantId }).then((res) => {
+    if (res.rows && res.rows.length > 0) {
+      userList.value = res.rows.map((item) => {
+        return {
+          text: item.nickName,
+          value: item.userId,
+        };
+      });
+    }
+  });
+};
+const getSource = () => {
+  return proxy
+    .post("/dictTenantData/page", {
+      pageNum: 1,
+      pageSize: 999,
+      tenantId: getUserInfo().tenantId,
+      dictCode: "customer_source",
+    })
+    .then((res) => {
+      customerSource.value = res.data.rows;
+    });
+};
+const getStatus = () => {
+  return proxy
+    .post("/dictTenantData/page", {
+      pageNum: 1,
+      pageSize: 999,
+      tenantId: getUserInfo().tenantId,
+      dictCode: "customer_status",
+    })
+    .then((res) => {
+      customerStatus.value = res.data.rows;
+    });
+};
+const getTag = () => {
+  return proxy
+    .post("/dictTenantData/page", {
+      pageNum: 1,
+      pageSize: 999,
+      tenantId: getUserInfo().tenantId,
+      dictCode: "customer_tag",
+    })
+    .then((res) => {
+      customerTag.value = res.data.rows;
+    });
+};
+onMounted(() => {
+  Promise.all([getUser(), getSource(), getStatus(), getTag()]).then(() => {
+    if (route.query.id) {
+      proxy.post("/customer/detail", { id: route.query.id }).then((res) => {
+        formData.data = res.data;
+        let cityText = "";
+        if (formData.data.countryName) {
+          cityText = formData.data.countryName;
+          if (formData.data.provinceName) {
+            cityText = cityText + "," + formData.data.provinceName;
+            if (formData.data.cityName) {
+              cityText = cityText + "," + formData.data.cityName;
+            }
+          }
+        }
+        formData.data.cityText = cityText;
+        let sourceText = "";
+        if (formData.data.source && customerSource.value && customerSource.value.length > 0) {
+          let list = customerSource.value.filter((item) => item.dictKey == formData.data.source);
+          if (list && list.length > 0) {
+            sourceText = list[0].dictValue;
+          }
+        }
+        formData.data.sourceText = sourceText;
+        let statusText = "";
+        if (formData.data.status && customerStatus.value && customerStatus.value.length > 0) {
+          let list = customerStatus.value.filter((item) => item.dictKey == formData.data.status);
+          if (list && list.length > 0) {
+            statusText = list[0].dictValue;
+          }
+        }
+        formData.data.statusText = statusText;
+        let userText = "";
+        if (formData.data.userId && userList.value && userList.value.length > 0) {
+          let list = userList.value.filter((item) => item.value == formData.data.userId);
+          if (list && list.length > 0) {
+            userText = list[0].text;
+          }
+        }
+        formData.data.userText = userText;
+        if (formData.data.tag) {
+          let tagText = "";
+          let tags = formData.data.tag.split(",");
+          if (tags && tags.length > 0) {
+            for (let i = 0; i < tags.length; i++) {
+              let list = customerTag.value.filter((item) => item.dictKey == tags[i]);
+              if (list && list.length > 0) {
+                if (i === 0) {
+                  tagText = list[0].dictValue;
+                } else {
+                  tagText = tagText + "," + list[0].dictValue;
+                }
+              }
+            }
+          }
+          formData.data.tagText = tagText;
+        }
+        proxy.post("/customerFollowRecords/page", { pageNum: 1, pageSize: 999, customerId: route.query.id }).then((resRecords) => {
+          formData.data.recordsList = resRecords.data.rows;
+        });
+      });
+    }
+  });
+});
+</script>

+ 93 - 0
src/views/customer/file/index.vue

@@ -0,0 +1,93 @@
+<template>
+  <van-nav-bar :title="$t('customerFile.name')" left-text="" left-arrow @click-left="onClickLeft" @click-right="onClickRight">
+    <template #right>{{ $t("common.add") }}</template>
+  </van-nav-bar>
+  <van-search v-model="req.keyword" :placeholder="$t('common.pleaseEnterKeywords')" @search="onRefresh" />
+  <van-pull-refresh v-model="loading" @refresh="onRefresh">
+    <div class="list">
+      <van-list v-model:loading="loading" :finished="finished" :finished-text="$t('common.noMore')" @load="getList" style="margin-bottom: 60px">
+        <commonList :data="listData" @onClick="toDtl" :config="listConfig"></commonList>
+      </van-list>
+    </div>
+  </van-pull-refresh>
+</template>
+<script setup>
+import { ref, getCurrentInstance } from "vue";
+import commonList from "@/components/common-list.vue";
+
+const proxy = getCurrentInstance().proxy;
+const onClickLeft = () => proxy.$router.push("/main/working");
+const onClickRight = () => {
+  proxy.$router.push({
+    path: "customerFileAdd",
+    query: {
+      type: "add",
+    },
+  });
+};
+const req = ref({
+  pageNum: 1,
+  keyword: null,
+});
+const finished = ref(false);
+const onRefresh = () => {
+  req.value.pageNum = 1;
+  finished.value = false;
+  getList("refresh");
+};
+const loading = ref(false);
+const listData = ref([]);
+const getList = (type) => {
+  loading.value = true;
+  proxy
+    .post("/customer/page", req.value)
+    .then((res) => {
+      if (res.data.rows && res.data.rows.length > 0) {
+        res.data.rows = res.data.rows.map((item) => {
+          return {
+            ...item,
+            cityText: item.countryName + "," + item.provinceName + "," + item.cityName,
+          };
+        });
+      }
+      listData.value = type === "refresh" ? res.data.rows : listData.value.concat(res.data.rows);
+      if (req.value.pageNum * 10 >= res.data.total) {
+        finished.value = true;
+      }
+      req.value.pageNum++;
+      loading.value = false;
+    })
+    .catch(() => {
+      loading.value = false;
+    });
+};
+const toDtl = (row) => {
+  proxy.$router.push({
+    path: "customerFileDetail",
+    query: {
+      id: row.id,
+      type: "detail",
+    },
+  });
+};
+const listConfig = ref([
+  {
+    label: proxy.t("customerFile.customerName"),
+    prop: "name",
+  },
+  {
+    label: proxy.t("customerFile.cityText"),
+    prop: "cityText",
+  },
+  {
+    label: proxy.t("customerFile.customerStatus"),
+    prop: "statusVal",
+  },
+]);
+</script>
+
+<style lang="scss" scoped>
+.list {
+  min-height: 70vh;
+}
+</style>

+ 94 - 0
src/views/customer/highseas/index.vue

@@ -0,0 +1,94 @@
+<template>
+  <van-nav-bar :title="$t('customerFile.highseasName')" left-text="" left-arrow @click-left="onClickLeft" @click-right="onClickRight">
+    <template #right>{{ $t("common.add") }}</template>
+  </van-nav-bar>
+  <van-search v-model="req.keyword" :placeholder="$t('common.pleaseEnterKeywords')" @search="onRefresh" />
+  <van-pull-refresh v-model="loading" @refresh="onRefresh">
+    <div class="list">
+      <van-list v-model:loading="loading" :finished="finished" :finished-text="$t('common.noMore')" @load="getList" style="margin-bottom: 60px">
+        <commonList :data="listData" @onClick="toDtl" :config="listConfig"></commonList>
+      </van-list>
+    </div>
+  </van-pull-refresh>
+</template>
+<script setup>
+import { ref, getCurrentInstance } from "vue";
+import commonList from "@/components/common-list.vue";
+
+const proxy = getCurrentInstance().proxy;
+const onClickLeft = () => proxy.$router.push("/main/working");
+const onClickRight = () => {
+  proxy.$router.push({
+    path: "customerFileAdd",
+    query: {
+      type: "add",
+    },
+  });
+};
+const req = ref({
+  pageNum: 1,
+  keyword: null,
+  type: 0,
+});
+const finished = ref(false);
+const onRefresh = () => {
+  req.value.pageNum = 1;
+  finished.value = false;
+  getList("refresh");
+};
+const loading = ref(false);
+const listData = ref([]);
+const getList = (type) => {
+  loading.value = true;
+  proxy
+    .post("/customer/page", req.value)
+    .then((res) => {
+      if (res.data.rows && res.data.rows.length > 0) {
+        res.data.rows = res.data.rows.map((item) => {
+          return {
+            ...item,
+            cityText: item.countryName + "," + item.provinceName + "," + item.cityName,
+          };
+        });
+      }
+      listData.value = type === "refresh" ? res.data.rows : listData.value.concat(res.data.rows);
+      if (req.value.pageNum * 10 >= res.data.total) {
+        finished.value = true;
+      }
+      req.value.pageNum++;
+      loading.value = false;
+    })
+    .catch(() => {
+      loading.value = false;
+    });
+};
+const toDtl = (row) => {
+  proxy.$router.push({
+    path: "customerFileDetail",
+    query: {
+      id: row.id,
+      type: "detail",
+    },
+  });
+};
+const listConfig = ref([
+  {
+    label: proxy.t("customerFile.customerName"),
+    prop: "name",
+  },
+  {
+    label: proxy.t("customerFile.cityText"),
+    prop: "cityText",
+  },
+  {
+    label: proxy.t("customerFile.customerStatus"),
+    prop: "statusVal",
+  },
+]);
+</script>
+
+<style lang="scss" scoped>
+.list {
+  min-height: 70vh;
+}
+</style>

+ 93 - 0
src/views/customer/privatesea/index.vue

@@ -0,0 +1,93 @@
+<template>
+  <van-nav-bar :title="$t('customerFile.privateseaName')" left-text="" left-arrow @click-left="onClickLeft" @click-right="onClickRight">
+    <template #right>{{ $t("common.add") }}</template>
+  </van-nav-bar>
+  <van-search v-model="req.keyword" :placeholder="$t('common.pleaseEnterKeywords')" @search="onRefresh" />
+  <van-pull-refresh v-model="loading" @refresh="onRefresh">
+    <div class="list">
+      <van-list v-model:loading="loading" :finished="finished" :finished-text="$t('common.noMore')" @load="getList" style="margin-bottom: 60px">
+        <commonList :data="listData" @onClick="toDtl" :config="listConfig"></commonList>
+      </van-list>
+    </div>
+  </van-pull-refresh>
+</template>
+<script setup>
+import { ref, getCurrentInstance } from "vue";
+import commonList from "@/components/common-list.vue";
+
+const proxy = getCurrentInstance().proxy;
+const onClickLeft = () => proxy.$router.push("/main/working");
+const onClickRight = () => {
+  proxy.$router.push({
+    path: "customerFileAdd",
+    query: {
+      type: "add",
+    },
+  });
+};
+const req = ref({
+  pageNum: 1,
+  keyword: null,
+});
+const finished = ref(false);
+const onRefresh = () => {
+  req.value.pageNum = 1;
+  finished.value = false;
+  getList("refresh");
+};
+const loading = ref(false);
+const listData = ref([]);
+const getList = (type) => {
+  loading.value = true;
+  proxy
+    .post("/customer/privateSeaPage", req.value)
+    .then((res) => {
+      if (res.data.rows && res.data.rows.length > 0) {
+        res.data.rows = res.data.rows.map((item) => {
+          return {
+            ...item,
+            cityText: item.countryName + "," + item.provinceName + "," + item.cityName,
+          };
+        });
+      }
+      listData.value = type === "refresh" ? res.data.rows : listData.value.concat(res.data.rows);
+      if (req.value.pageNum * 10 >= res.data.total) {
+        finished.value = true;
+      }
+      req.value.pageNum++;
+      loading.value = false;
+    })
+    .catch(() => {
+      loading.value = false;
+    });
+};
+const toDtl = (row) => {
+  proxy.$router.push({
+    path: "customerFileDetail",
+    query: {
+      id: row.id,
+      type: "detail",
+    },
+  });
+};
+const listConfig = ref([
+  {
+    label: proxy.t("customerFile.customerName"),
+    prop: "name",
+  },
+  {
+    label: proxy.t("customerFile.cityText"),
+    prop: "cityText",
+  },
+  {
+    label: proxy.t("customerFile.customerStatus"),
+    prop: "statusVal",
+  },
+]);
+</script>
+
+<style lang="scss" scoped>
+.list {
+  min-height: 70vh;
+}
+</style>

+ 254 - 0
src/views/fund/account-payment/add.vue

@@ -0,0 +1,254 @@
+<template>
+  <div class="form">
+    <van-nav-bar :title="$t('accountPayment.name')" :left-text="$t('common.back')" left-arrow @click-left="onClickLeft"> </van-nav-bar>
+    <testForm v-model="formData.data" :formOption="formOption" :formConfig="formConfig" :rules="rules" @onSubmit="onSubmit" ref="formDom">
+      <template #date>
+        <div style="width: 100%">
+          <van-cell-group inset>
+            <van-field
+              v-model="formData.data.expensesTime"
+              is-link
+              readonly
+              :label="$t('accountPayment.expensesTime')"
+              :placeholder="$t('common.pleaseSelect')"
+              style="padding: 0 !important"
+              :required="true"
+              @click="clickDate" />
+            <van-popup v-model:show="showPicker" round position="bottom">
+              <van-picker-group
+                :title="$t('accountPayment.expensesTime')"
+                :tabs="[$t('common.selectDate'), $t('common.selectTime')]"
+                @confirm="onConfirm"
+                @cancel="onCancel">
+                <van-date-picker v-model="currentDate" />
+                <van-time-picker v-model="currentTime" :columns-type="columnsType" />
+              </van-picker-group>
+            </van-popup>
+          </van-cell-group>
+        </div>
+      </template>
+    </testForm>
+  </div>
+</template>
+
+<script setup>
+import { ref, getCurrentInstance, onMounted, reactive } from "vue";
+import { showSuccessToast } from "vant";
+import { useRoute } from "vue-router";
+import { getUserInfo, formatDate } from "@/utils/auth";
+import testForm from "@/components/testForm/index.vue";
+
+const proxy = getCurrentInstance().proxy;
+const onClickLeft = () => history.back();
+const route = useRoute();
+const showPicker = ref(false);
+const currentDate = ref([]);
+const currentTime = ref([]);
+const columnsType = ["hour", "minute", "second"];
+const onConfirm = () => {
+  formData.data.expensesTime = currentDate.value.join("-") + " " + currentTime.value.join(":");
+  showPicker.value = false;
+};
+const onCancel = () => {
+  showPicker.value = false;
+};
+const clickDate = () => {
+  currentDate.value = formatDate(new Date(formData.data.expensesTime), "yyyy-MM-dd").split("-");
+  currentTime.value = formatDate(new Date(formData.data.expensesTime), "hh:mm:ss").split(":");
+  showPicker.value = true;
+};
+const formData = reactive({
+  data: {
+    expensesTime: "",
+  },
+});
+const formDom = ref(null);
+const formOption = reactive({
+  readonly: false, //用于控制整个表单是否只读
+  disabled: false,
+  labelAlign: "top",
+  scroll: true,
+  labelWidth: "62pk",
+  hiddenSubmitBtn: false,
+});
+const formConfig = reactive([
+  {
+    type: "title",
+    title: proxy.t("accountPayment.requestInformation"),
+  },
+  {
+    type: "input",
+    label: proxy.t("accountPayment.businessManagementName"),
+    prop: "businessManagementName",
+    itemType: "text",
+    readonly: true,
+  },
+  {
+    type: "input",
+    label: proxy.t("accountPayment.currencyAmount"),
+    prop: "currencyAmount",
+    itemType: "text",
+    readonly: true,
+  },
+  {
+    type: "input",
+    label: proxy.t("accountPayment.paymentMethodText"),
+    prop: "paymentMethodText",
+    itemType: "text",
+    readonly: true,
+  },
+  {
+    type: "input",
+    label: proxy.t("accountPayment.collectionName"),
+    prop: "name",
+    itemType: "text",
+    readonly: true,
+  },
+  {
+    type: "input",
+    label: proxy.t("accountPayment.accountOpening"),
+    prop: "accountOpening",
+    itemType: "text",
+    readonly: true,
+  },
+  {
+    type: "input",
+    label: proxy.t("accountPayment.openingBank"),
+    prop: "openingBank",
+    itemType: "text",
+    readonly: true,
+  },
+  {
+    type: "input",
+    label: proxy.t("accountPayment.interbankNumber"),
+    prop: "interbankNumber",
+    itemType: "text",
+    readonly: true,
+  },
+  {
+    type: "title",
+    title: proxy.t("accountPayment.paymentInformation"),
+  },
+  {
+    type: "picker",
+    label: proxy.t("accountPayment.accountManagementId"),
+    prop: "accountManagementId",
+    itemType: "onePicker",
+    showPicker: false,
+    fieldNames: {
+      text: "label",
+      value: "value",
+    },
+    data: [],
+  },
+  {
+    type: "picker",
+    label: proxy.t("accountPayment.currency"),
+    prop: "currency",
+    itemType: "onePicker",
+    showPicker: false,
+    readonly: true,
+    fieldNames: {
+      text: "label",
+      value: "value",
+    },
+    data: [],
+  },
+  {
+    type: "input",
+    label: proxy.t("accountPayment.amount"),
+    prop: "amount",
+    itemType: "number",
+    readonly: true,
+  },
+  {
+    type: "slot",
+    slotName: "date",
+  },
+  {
+    type: "input",
+    label: proxy.t("accountPayment.remark"),
+    prop: "remark",
+    itemType: "textarea",
+  },
+]);
+const rules = {
+  accountManagementId: [{ required: true, message: proxy.t("accountPayment.accountManagementIdMsg") }],
+  currency: [{ required: true, message: proxy.t("accountPayment.currencyMsg") }],
+  amount: [{ required: true, message: proxy.t("accountPayment.amountMsg") }],
+  expensesTime: [{ required: true, message: proxy.t("accountPayment.expensesTimeMsg") }],
+};
+const onSubmit = () => {
+  proxy.post("/accountPayment/add", formData.data).then(() => {
+    showSuccessToast(proxy.t("common.addSuccess"));
+    setTimeout(() => {
+      history.back();
+    }, 500);
+  });
+};
+const fundsPayment = ref([]);
+const gerFundsPayment = () => {
+  return proxy
+    .post("/dictTenantData/page", {
+      pageNum: 1,
+      pageSize: 999,
+      tenantId: getUserInfo().tenantId,
+      dictCode: "funds_payment_method",
+    })
+    .then((res) => {
+      fundsPayment.value = res.data.rows;
+    });
+};
+const accountList = ref([]);
+const gerAccountList = () => {
+  return proxy.post("/accountManagement/page", { pageNum: 1, pageSize: 9999 }).then((res) => {
+    if (res.data.rows && res.data.rows.length > 0) {
+      accountList.value = res.data.rows.map((item) => {
+        return {
+          label: item.alias + " (" + item.name + ")",
+          value: item.id,
+        };
+      });
+      formConfig[9].data = accountList.value;
+    }
+  });
+};
+const gerCurrency = () => {
+  return proxy
+    .post("/dictTenantData/page", {
+      pageNum: 1,
+      pageSize: 999,
+      tenantId: getUserInfo().tenantId,
+      dictCode: "account_currency",
+    })
+    .then((res) => {
+      if (res.data.rows && res.data.rows.length > 0) {
+        formConfig[10].data = res.data.rows.map((item) => {
+          return {
+            label: item.dictValue,
+            value: item.dictKey,
+          };
+        });
+      }
+    });
+};
+onMounted(() => {
+  Promise.all([gerFundsPayment(), gerAccountList(), gerCurrency()]).then(() => {
+    if (route.query.id) {
+      proxy.post("/accountPayment/detail", { id: route.query.id }).then((res) => {
+        formData.data = res.data;
+        formData.data.expensesTime = formatDate(new Date(), "yyyy-MM-dd hh:mm:ss");
+        formData.data.currencyAmount = formData.data.currency + " " + formData.data.amount;
+        let paymentMethodText = "";
+        if (formData.data.paymentMethod && fundsPayment.value && fundsPayment.value.length > 0) {
+          let list = fundsPayment.value.filter((item) => item.dictKey == formData.data.paymentMethod);
+          if (list && list.length > 0) {
+            paymentMethodText = list[0].dictValue;
+          }
+        }
+        formData.data.paymentMethodText = paymentMethodText;
+      });
+    }
+  });
+});
+</script>

+ 204 - 0
src/views/fund/account-payment/detail.vue

@@ -0,0 +1,204 @@
+<template>
+  <div class="form">
+    <van-nav-bar :title="$t('accountPayment.name')" :left-text="$t('common.back')" left-arrow @click-left="onClickLeft"> </van-nav-bar>
+    <testForm v-model="formData.data" :formOption="formOption" :formConfig="formConfig" :rules="rules" ref="formDom"> </testForm>
+  </div>
+</template>
+
+<script setup>
+import { ref, getCurrentInstance, onMounted, reactive } from "vue";
+import { useRoute } from "vue-router";
+import { getUserInfo } from "@/utils/auth";
+import testForm from "@/components/testForm/index.vue";
+
+const proxy = getCurrentInstance().proxy;
+const onClickLeft = () => history.back();
+const route = useRoute();
+const formData = reactive({
+  data: {},
+});
+const formDom = ref(null);
+const formOption = reactive({
+  readonly: false, //用于控制整个表单是否只读
+  disabled: false,
+  labelAlign: "top",
+  scroll: true,
+  labelWidth: "62pk",
+  hiddenSubmitBtn: true,
+});
+const formConfig = reactive([
+  {
+    type: "title",
+    title: proxy.t("accountPayment.requestInformation"),
+  },
+  {
+    type: "input",
+    label: proxy.t("accountPayment.businessManagementName"),
+    prop: "businessManagementName",
+    itemType: "text",
+    readonly: true,
+  },
+  {
+    type: "input",
+    label: proxy.t("accountPayment.currencyAmount"),
+    prop: "currencyAmount",
+    itemType: "text",
+    readonly: true,
+  },
+  {
+    type: "input",
+    label: proxy.t("accountPayment.paymentMethodText"),
+    prop: "paymentMethodText",
+    itemType: "text",
+    readonly: true,
+  },
+  {
+    type: "input",
+    label: proxy.t("accountPayment.collectionName"),
+    prop: "name",
+    itemType: "text",
+    readonly: true,
+  },
+  {
+    type: "input",
+    label: proxy.t("accountPayment.accountOpening"),
+    prop: "accountOpening",
+    itemType: "text",
+    readonly: true,
+  },
+  {
+    type: "input",
+    label: proxy.t("accountPayment.openingBank"),
+    prop: "openingBank",
+    itemType: "text",
+    readonly: true,
+  },
+  {
+    type: "input",
+    label: proxy.t("accountPayment.interbankNumber"),
+    prop: "interbankNumber",
+    itemType: "text",
+    readonly: true,
+  },
+  {
+    type: "title",
+    title: proxy.t("accountPayment.paymentInformation"),
+  },
+  {
+    type: "picker",
+    label: proxy.t("accountPayment.accountManagementId"),
+    prop: "accountManagementId",
+    itemType: "onePicker",
+    showPicker: false,
+    readonly: true,
+    fieldNames: {
+      text: "label",
+      value: "value",
+    },
+    data: [],
+  },
+  {
+    type: "picker",
+    label: proxy.t("accountPayment.currency"),
+    prop: "currency",
+    itemType: "onePicker",
+    showPicker: false,
+    readonly: true,
+    fieldNames: {
+      text: "label",
+      value: "value",
+    },
+    data: [],
+  },
+  {
+    type: "input",
+    label: proxy.t("accountPayment.amount"),
+    prop: "amount",
+    itemType: "number",
+    readonly: true,
+  },
+  {
+    type: "input",
+    label: proxy.t("accountPayment.expensesTime"),
+    prop: "expensesTime",
+    readonly: true,
+  },
+  {
+    type: "input",
+    label: proxy.t("accountPayment.remark"),
+    prop: "remark",
+    itemType: "textarea",
+    readonly: true,
+  },
+]);
+const rules = {
+  accountManagementId: [{ required: true, message: proxy.t("accountPayment.accountManagementIdMsg") }],
+  currency: [{ required: true, message: proxy.t("accountPayment.currencyMsg") }],
+  amount: [{ required: true, message: proxy.t("accountPayment.amountMsg") }],
+  expensesTime: [{ required: true, message: proxy.t("accountPayment.expensesTimeMsg") }],
+};
+const fundsPayment = ref([]);
+const gerFundsPayment = () => {
+  return proxy
+    .post("/dictTenantData/page", {
+      pageNum: 1,
+      pageSize: 999,
+      tenantId: getUserInfo().tenantId,
+      dictCode: "funds_payment_method",
+    })
+    .then((res) => {
+      fundsPayment.value = res.data.rows;
+    });
+};
+const accountList = ref([]);
+const gerAccountList = () => {
+  return proxy.post("/accountManagement/page", { pageNum: 1, pageSize: 9999 }).then((res) => {
+    if (res.data.rows && res.data.rows.length > 0) {
+      accountList.value = res.data.rows.map((item) => {
+        return {
+          label: item.alias + " (" + item.name + ")",
+          value: item.id,
+        };
+      });
+      formConfig[9].data = accountList.value;
+    }
+  });
+};
+const gerCurrency = () => {
+  return proxy
+    .post("/dictTenantData/page", {
+      pageNum: 1,
+      pageSize: 999,
+      tenantId: getUserInfo().tenantId,
+      dictCode: "account_currency",
+    })
+    .then((res) => {
+      if (res.data.rows && res.data.rows.length > 0) {
+        formConfig[10].data = res.data.rows.map((item) => {
+          return {
+            label: item.dictValue,
+            value: item.dictKey,
+          };
+        });
+      }
+    });
+};
+onMounted(() => {
+  Promise.all([gerFundsPayment(), gerAccountList(), gerCurrency()]).then(() => {
+    if (route.query.id) {
+      proxy.post("/accountPayment/detail", { id: route.query.id }).then((res) => {
+        formData.data = res.data;
+        formData.data.currencyAmount = formData.data.currency + " " + formData.data.amount;
+        let paymentMethodText = "";
+        if (formData.data.paymentMethod && fundsPayment.value && fundsPayment.value.length > 0) {
+          let list = fundsPayment.value.filter((item) => item.dictKey == formData.data.paymentMethod);
+          if (list && list.length > 0) {
+            paymentMethodText = list[0].dictValue;
+          }
+        }
+        formData.data.paymentMethodText = paymentMethodText;
+      });
+    }
+  });
+});
+</script>

+ 106 - 0
src/views/fund/account-payment/index.vue

@@ -0,0 +1,106 @@
+<template>
+  <van-nav-bar :title="$t('accountPayment.name')" left-text="" left-arrow @click-left="onClickLeft"> </van-nav-bar>
+  <van-search v-model="req.keyword" :placeholder="$t('common.pleaseEnterKeywords')" @search="onRefresh" />
+  <van-pull-refresh v-model="loading" @refresh="onRefresh">
+    <div class="list">
+      <van-list v-model:loading="loading" :finished="finished" :finished-text="$t('common.noMore')" @load="getList" style="margin-bottom: 60px">
+        <commonList :data="listData" @onClick="toDtl" :config="listConfig"></commonList>
+      </van-list>
+    </div>
+  </van-pull-refresh>
+</template>
+<script setup>
+import { ref, getCurrentInstance } from "vue";
+import commonList from "@/components/common-list.vue";
+
+const proxy = getCurrentInstance().proxy;
+const onClickLeft = () => proxy.$router.push("/main/working");
+const req = ref({
+  pageNum: 1,
+  keyword: null,
+});
+const finished = ref(false);
+const onRefresh = () => {
+  req.value.pageNum = 1;
+  finished.value = false;
+  getList("refresh");
+};
+const loading = ref(false);
+const listData = ref([]);
+const getList = (type) => {
+  loading.value = true;
+  proxy
+    .post("/accountPayment/page", req.value)
+    .then((res) => {
+      if (res.data.rows && res.data.rows.length > 0) {
+        res.data.rows = res.data.rows.map((item) => {
+          let statusText = "";
+          if (item.status == "10") {
+            statusText = "已打款";
+          } else if (item.status == "20") {
+            statusText = "未打款";
+          }
+          return {
+            ...item,
+            currencyAmount: item.currency + " " + item.amount,
+            statusText: statusText,
+          };
+        });
+      }
+      listData.value = type === "refresh" ? res.data.rows : listData.value.concat(res.data.rows);
+      if (req.value.pageNum * 10 >= res.data.total) {
+        finished.value = true;
+      }
+      req.value.pageNum++;
+      loading.value = false;
+    })
+    .catch(() => {
+      loading.value = false;
+    });
+};
+const toDtl = (row) => {
+  if (row.status == "10") {
+    proxy.$router.push({
+      path: "accountPaymentDetail",
+      query: {
+        id: row.id,
+      },
+    });
+  } else if (row.status == "20") {
+    proxy.$router.push({
+      path: "accountPaymentAdd",
+      query: {
+        id: row.id,
+      },
+    });
+  }
+};
+const listConfig = ref([
+  {
+    label: proxy.t("accountPayment.corporationName"),
+    prop: "corporationName",
+  },
+  {
+    label: proxy.t("accountPayment.createTime"),
+    prop: "createTime",
+  },
+  {
+    label: proxy.t("accountPayment.currencyAmount"),
+    prop: "currencyAmount",
+  },
+  {
+    label: proxy.t("accountPayment.paymentRemark"),
+    prop: "paymentRemark",
+  },
+  {
+    label: proxy.t("accountPayment.statusText"),
+    prop: "statusText",
+  },
+]);
+</script>
+
+<style lang="scss" scoped>
+.list {
+  min-height: 70vh;
+}
+</style>

+ 249 - 0
src/views/fund/account/add.vue

@@ -0,0 +1,249 @@
+<template>
+  <div class="form">
+    <van-nav-bar :title="$t('account.' + route.query.type)" :left-text="$t('common.back')" left-arrow @click-left="onClickLeft"> </van-nav-bar>
+    <testForm v-model="formData.data" :formOption="formOption" :formConfig="formConfig" :rules="rules" @onSubmit="onSubmit" ref="formDom"> </testForm>
+  </div>
+</template>
+
+<script setup>
+import { ref, getCurrentInstance, onMounted, reactive } from "vue";
+import { showSuccessToast, showFailToast } from "vant";
+import { useRoute } from "vue-router";
+import { getUserInfo } from "@/utils/auth";
+import testForm from "@/components/testForm/index.vue";
+
+const proxy = getCurrentInstance().proxy;
+const onClickLeft = () => history.back();
+const route = useRoute();
+const getDict = () => {
+  proxy
+    .post("/dictTenantData/page", {
+      pageNum: 1,
+      pageSize: 999,
+      dictCode: "account_currency",
+      tenantId: getUserInfo().tenantId,
+    })
+    .then((res) => {
+      if (res.data.rows && res.data.rows.length > 0) {
+        formOption.btnConfig.listConfig[0].data = res.data.rows.map((item) => {
+          return {
+            text: item.dictValue,
+            value: item.dictKey,
+          };
+        });
+      }
+    });
+  proxy.post("/corporation/page", { pageNum: 1, pageSize: 9999 }).then((res) => {
+    if (res.data.rows && res.data.rows.length > 0) {
+      formConfig[0].data = res.data.rows.map((item) => {
+        return {
+          ...item,
+          label: item.name,
+          value: item.id,
+        };
+      });
+    }
+  });
+};
+getDict();
+const formData = reactive({
+  data: {
+    corporationId: null,
+    alias: null,
+    name: null,
+    openingBank: null,
+    accountOpening: null,
+    interbankNumber: null,
+    beneficiaryName: null,
+    beneficiaryBank: null,
+    beneficiaryBankAddress: null,
+    beneficiaryAccountNumber: null,
+    swiftCode: null,
+    beneficiaryAddress: null,
+    accountRemainderList: [],
+  },
+});
+const formDom = ref(null);
+const formOption = reactive({
+  readonly: false, //用于控制整个表单是否只读
+  disabled: false,
+  labelAlign: "top",
+  scroll: true,
+  labelWidth: "62pk",
+  hiddenSubmitBtn: false,
+  btnConfig: {
+    isNeed: true,
+    prop: "accountRemainderList",
+    plain: true,
+    listTitle: proxy.t("account.accountBalance"),
+    listConfig: [
+      {
+        type: "picker",
+        label: proxy.t("account.currency"),
+        prop: "currency",
+        itemType: "onePicker",
+        showPicker: false,
+        readonly: false,
+        fieldNames: {
+          text: "text",
+          value: "value",
+        },
+        data: [],
+      },
+      {
+        type: "input",
+        label: proxy.t("account.remainder"),
+        prop: "remainder",
+        itemType: "number",
+      },
+    ],
+    clickFn: () => {
+      if (formData.data.accountRemainderList && formData.data.accountRemainderList.length > 0) {
+        formData.data.accountRemainderList.push({
+          currency: null,
+          remainder: null,
+        });
+      } else {
+        formData.data.accountRemainderList = [
+          {
+            currency: null,
+            remainder: null,
+          },
+        ];
+      }
+    },
+  },
+});
+const formConfig = reactive([
+  {
+    type: "picker",
+    label: proxy.t("account.company"),
+    prop: "corporationId",
+    itemType: "onePicker",
+    showPicker: false,
+    fieldNames: {
+      text: "label",
+      value: "value",
+    },
+    data: [],
+  },
+  {
+    type: "input",
+    label: proxy.t("account.alias"),
+    prop: "alias",
+    itemType: "text",
+  },
+  {
+    type: "input",
+    label: proxy.t("account.openingBank"),
+    prop: "openingBank",
+    itemType: "text",
+  },
+  {
+    type: "input",
+    label: proxy.t("account.accountName"),
+    prop: "name",
+    itemType: "text",
+  },
+  {
+    type: "input",
+    label: proxy.t("account.accountOpening"),
+    prop: "accountOpening",
+    itemType: "text",
+  },
+  {
+    type: "input",
+    label: proxy.t("account.interbankNumber"),
+    prop: "interbankNumber",
+    itemType: "text",
+  },
+  {
+    type: "title",
+    title: proxy.t("account.foreignExchange"),
+  },
+  {
+    type: "input",
+    label: proxy.t("account.beneficiaryName"),
+    prop: "beneficiaryName",
+    itemType: "text",
+  },
+  {
+    type: "input",
+    label: proxy.t("account.beneficiaryBank"),
+    prop: "beneficiaryBank",
+    itemType: "text",
+  },
+  {
+    type: "input",
+    label: proxy.t("account.beneficiaryBankAddress"),
+    prop: "beneficiaryBankAddress",
+    itemType: "text",
+  },
+  {
+    type: "input",
+    label: proxy.t("account.beneficiaryAccountNumber"),
+    prop: "beneficiaryAccountNumber",
+    itemType: "text",
+  },
+  {
+    type: "input",
+    label: proxy.t("account.swiftCode"),
+    prop: "swiftCode",
+    itemType: "text",
+  },
+  {
+    type: "input",
+    label: proxy.t("account.beneficiaryAddress"),
+    prop: "beneficiaryAddress",
+    itemType: "text",
+  },
+]);
+const rules = {
+  corporationId: [{ required: true, message: proxy.t("account.companyMsg") }],
+  alias: [{ required: true, message: proxy.t("account.aliasMsg") }],
+  openingBank: [{ required: true, message: proxy.t("account.openingBankMsg") }],
+  name: [{ required: true, message: proxy.t("account.accountNameMsg") }],
+  accountOpening: [{ required: true, message: proxy.t("account.accountOpeningMsg") }],
+  currency: [{ required: true, message: proxy.t("account.currencyMsg") }],
+  remainder: [{ required: true, message: proxy.t("account.remainderMsg") }],
+};
+const isRepeat = (arr) => {
+  var hash = {};
+  for (var i in arr) {
+    if (hash[arr[i].currency]) return true;
+    hash[arr[i].currency] = true;
+  }
+  return false;
+};
+const onSubmit = () => {
+  if (formData.data.accountRemainderList && formData.data.accountRemainderList.length > 0) {
+    if (isRepeat(formData.data.accountRemainderList)) {
+      showFailToast(proxy.t("account.submitMsgTwo"));
+    } else {
+      proxy.post("/accountManagement/" + route.query.type, formData.data).then(() => {
+        showSuccessToast(proxy.t("common.addSuccess"));
+        setTimeout(() => {
+          history.back();
+        }, 500);
+      });
+    }
+  } else {
+    showFailToast(proxy.t("account.submitMsgOne"));
+  }
+};
+onMounted(() => {
+  if (route.query.id) {
+    proxy.post("/accountManagement/detail", { id: route.query.id }).then((res) => {
+      formData.data = res.data;
+      if (formData.data.accountRemainderList && formData.data.accountRemainderList.length > 0) {
+        for (let i = 0; i < formData.data.accountRemainderList.length; i++) {
+          let data = formOption.btnConfig.listConfig[0].data.filter((item) => item.value === formData.data.accountRemainderList[i].currency);
+          if (data && data.length > 0) {
+            formData.data.accountRemainderList[i].currencyName = data[0].text;
+          }
+        }
+      }
+    });
+  }
+});
+</script>

+ 85 - 0
src/views/fund/account/index.vue

@@ -0,0 +1,85 @@
+<template>
+  <van-nav-bar :title="$t('account.name')" left-text="" left-arrow @click-left="onClickLeft" @click-right="onClickRight">
+    <template #right>{{ $t("common.add") }}</template>
+  </van-nav-bar>
+  <van-search v-model="req.keyword" :placeholder="$t('common.pleaseEnterKeywords')" @search="onRefresh" />
+  <van-pull-refresh v-model="loading" @refresh="onRefresh">
+    <div class="list">
+      <van-list v-model:loading="loading" :finished="finished" :finished-text="$t('common.noMore')" @load="getList" style="margin-bottom: 60px">
+        <commonList :data="listData" @onClick="toDtl" :config="listConfig"></commonList>
+      </van-list>
+    </div>
+  </van-pull-refresh>
+</template>
+<script setup>
+import { ref, getCurrentInstance } from "vue";
+import commonList from "@/components/common-list.vue";
+
+const proxy = getCurrentInstance().proxy;
+const onClickLeft = () => proxy.$router.push("/main/working");
+const onClickRight = () => {
+  proxy.$router.push({
+    path: "accountAdd",
+    query: {
+      type: "add",
+    },
+  });
+};
+const req = ref({
+  pageNum: 1,
+  keyword: null,
+});
+const finished = ref(false);
+const onRefresh = () => {
+  req.value.pageNum = 1;
+  finished.value = false;
+  getList("refresh");
+};
+const loading = ref(false);
+const listData = ref([]);
+const getList = (type) => {
+  loading.value = true;
+  proxy
+    .post("/accountManagement/page", req.value)
+    .then((res) => {
+      listData.value = type === "refresh" ? res.data.rows : listData.value.concat(res.data.rows);
+      if (req.value.pageNum * 10 >= res.data.total) {
+        finished.value = true;
+      }
+      req.value.pageNum++;
+      loading.value = false;
+    })
+    .catch(() => {
+      loading.value = false;
+    });
+};
+const toDtl = (row) => {
+  proxy.$router.push({
+    path: "accountAdd",
+    query: {
+      id: row.id,
+      type: "edit",
+    },
+  });
+};
+const listConfig = ref([
+  {
+    label: proxy.t("account.company"),
+    prop: "corporationName",
+  },
+  {
+    label: proxy.t("account.alias"),
+    prop: "alias",
+  },
+  {
+    label: proxy.t("account.accountOpening"),
+    prop: "accountOpening",
+  },
+]);
+</script>
+
+<style lang="scss" scoped>
+.list {
+  min-height: 70vh;
+}
+</style>

+ 105 - 110
src/views/fund/flow-of-funds/index.vue

@@ -1,114 +1,109 @@
 <template>
-    <van-nav-bar :title="$t('flowFunds.name')" left-text="" left-arrow @click-left="onClickLeft" @click-right="onClickRight">
-      <template #right>{{ $t("common.add") }}</template>
-    </van-nav-bar>
-    <van-search v-model="req.keyword" :placeholder="$t('common.pleaseEnterKeywords')" @search="onRefresh" />
-    <van-pull-refresh v-model="loading" @refresh="onRefresh">
-      <div class="list">
-        <van-list v-model:loading="loading" :finished="finished" :finished-text="$t('common.noMore')" @load="getList" style="margin-bottom: 60px">
-          <commonList :data="listData" @onClick="toDtl" :config="listConfig"></commonList>
-        </van-list>
-      </div>
-    </van-pull-refresh>
-  </template>
-  <script setup>
-  import { ref, getCurrentInstance } from "vue";
-  import commonList from "@/components/common-list.vue";
-  
-  const proxy = getCurrentInstance().proxy;
-  const onClickLeft = () => proxy.$router.push("/main/working");
-  const onClickRight = () => {
-    proxy.$router.push({
-      path: "flowOfFundsAdd",
-      query: {
-        type: "add",
-      },
-    });
-  };
-  const req = ref({
-    pageNum: 1,
-    keyword: null,
+  <van-nav-bar :title="$t('flowFunds.name')" left-text="" left-arrow @click-left="onClickLeft" @click-right="onClickRight">
+    <template #right>{{ $t("common.add") }}</template>
+  </van-nav-bar>
+  <van-search v-model="req.keyword" :placeholder="$t('common.pleaseEnterKeywords')" @search="onRefresh" />
+  <van-pull-refresh v-model="loading" @refresh="onRefresh">
+    <div class="list">
+      <van-list v-model:loading="loading" :finished="finished" :finished-text="$t('common.noMore')" @load="getList" style="margin-bottom: 60px">
+        <commonList :data="listData" @onClick="toDtl" :config="listConfig"></commonList>
+      </van-list>
+    </div>
+  </van-pull-refresh>
+</template>
+<script setup>
+import { ref, getCurrentInstance } from "vue";
+import commonList from "@/components/common-list.vue";
+
+const proxy = getCurrentInstance().proxy;
+const onClickLeft = () => proxy.$router.push("/main/working");
+const onClickRight = () => {
+  proxy.$router.push({
+    path: "flowOfFundsAdd",
+    query: {
+      type: "add",
+    },
   });
-  const finished = ref(false);
-  const onRefresh = () => {
-    req.value.pageNum = 1;
-    finished.value = false;
-    getList("refresh");
-  };
-  const loading = ref(false);
-  const listData = ref([]);
-  const getList = (type) => {
-    loading.value = true;
-    proxy
-      .post("/accountRunningWater/page", req.value)
-      .then((res) => {
-        if (res.data.rows && res.data.rows.length > 0) {
-          res.data.rows = res.data.rows.map((item) => {
-            let status = "";
-            let style = "color: #04cb04";
-            if (item.status == 20) {
-              status = "-";
-              style = "color: red";
-            }
-            let currency = "";
-            if (item.currency) {
-              currency = item.currency;
-            }
-            return {
-              ...item,
-              currencyAmount: currency + " " + status + item.amount,
-              style: style,
-            };
-          });
-        }
-        listData.value = type === "refresh" ? res.data.rows : listData.value.concat(res.data.rows);
-        if (req.value.pageNum * 10 >= res.data.total) {
-          finished.value = true;
-        }
-        req.value.pageNum++;
-        loading.value = false;
-      })
-      .catch(() => {
-        loading.value = false;
-      });
-  };
-  const toDtl = (row) => {
-    proxy.$router.push({
-      path: "flowOfFundsAdd",
-      query: {
-        id: row.id,
-        type: "edit",
-      },
+};
+const req = ref({
+  pageNum: 1,
+  keyword: null,
+});
+const finished = ref(false);
+const onRefresh = () => {
+  req.value.pageNum = 1;
+  finished.value = false;
+  getList("refresh");
+};
+const loading = ref(false);
+const listData = ref([]);
+const getList = (type) => {
+  loading.value = true;
+  proxy
+    .post("/accountRunningWater/page", req.value)
+    .then((res) => {
+      if (res.data.rows && res.data.rows.length > 0) {
+        res.data.rows = res.data.rows.map((item) => {
+          let status = "";
+          let style = "color: #04cb04";
+          if (item.status == 20) {
+            status = "-";
+            style = "color: red";
+          }
+          return {
+            ...item,
+            currencyAmount: item.currency + " " + status + item.amount,
+            style: style,
+          };
+        });
+      }
+      listData.value = type === "refresh" ? res.data.rows : listData.value.concat(res.data.rows);
+      if (req.value.pageNum * 10 >= res.data.total) {
+        finished.value = true;
+      }
+      req.value.pageNum++;
+      loading.value = false;
+    })
+    .catch(() => {
+      loading.value = false;
     });
-  };
-  const listConfig = ref([
-    {
-      label: proxy.t("flowFunds.company"),
-      prop: "corporationName",
-    },
-    {
-      label: proxy.t("flowFunds.account"),
-      prop: "accountManagementName",
-    },
-    {
-      label: proxy.t("flowFunds.amount"),
-      prop: "currencyAmount",
-      style: "style",
+};
+const toDtl = (row) => {
+  proxy.$router.push({
+    path: "flowOfFundsAdd",
+    query: {
+      id: row.id,
+      type: "edit",
     },
-    {
-      label: proxy.t("flowFunds.tradingHour"),
-      prop: "transactionTime",
-    },
-    {
-      label: proxy.t("flowFunds.digest"),
-      prop: "remarks",
-    },
-  ]);
-  </script>
-  
-  <style lang="scss" scoped>
-  .list {
-    min-height: 70vh;
-  }
-  </style>
-  
+  });
+};
+const listConfig = ref([
+  {
+    label: proxy.t("flowFunds.company"),
+    prop: "corporationName",
+  },
+  {
+    label: proxy.t("flowFunds.account"),
+    prop: "accountManagementName",
+  },
+  {
+    label: proxy.t("flowFunds.amount"),
+    prop: "currencyAmount",
+    style: "style",
+  },
+  {
+    label: proxy.t("flowFunds.tradingHour"),
+    prop: "transactionTime",
+  },
+  {
+    label: proxy.t("flowFunds.digest"),
+    prop: "remarks",
+  },
+]);
+</script>
+
+<style lang="scss" scoped>
+.list {
+  min-height: 70vh;
+}
+</style>

+ 119 - 0
src/views/fund/funds/index.vue

@@ -0,0 +1,119 @@
+<template>
+  <van-nav-bar :title="$t('funds.name')" left-text="" left-arrow @click-left="onClickLeft" @click-right="onClickRight">
+    <template #right>{{ $t("common.add") }}</template>
+  </van-nav-bar>
+  <van-search v-model="req.keyword" :placeholder="$t('common.pleaseEnterKeywords')" @search="onRefresh" />
+  <van-pull-refresh v-model="loading" @refresh="onRefresh">
+    <div class="list">
+      <van-list v-model:loading="loading" :finished="finished" :finished-text="$t('common.noMore')" @load="getList" style="margin-bottom: 60px">
+        <commonList :data="listData" @onClick="toDtl" :config="listConfig"></commonList>
+      </van-list>
+    </div>
+  </van-pull-refresh>
+</template>
+<script setup>
+import { ref, getCurrentInstance } from "vue";
+import commonList from "@/components/common-list.vue";
+
+const proxy = getCurrentInstance().proxy;
+const onClickLeft = () => proxy.$router.push("/main/working");
+const onClickRight = () => {
+  // proxy.$router.push({
+  //   path: "flowOfFundsAdd",
+  //   query: {
+  //     type: "add",
+  //   },
+  // });
+};
+const req = ref({
+  pageNum: 1,
+  keyword: null,
+});
+const finished = ref(false);
+const onRefresh = () => {
+  req.value.pageNum = 1;
+  finished.value = false;
+  getList("refresh");
+};
+const loading = ref(false);
+const listData = ref([]);
+const getList = (type) => {
+  loading.value = true;
+  proxy
+    .post("/accountRequestFunds/page", req.value)
+    .then((res) => {
+      if (res.data.rows && res.data.rows.length > 0) {
+        res.data.rows = res.data.rows.map((item) => {
+          let fundsText = "";
+          if (item.status == 10) {
+            fundsText = "审批中";
+          } else if (item.status == 20) {
+            fundsText = "驳回";
+          } else if (item.status == 30) {
+            fundsText = "审批通过";
+          }
+          if (item.accountPaymentStatus == "10") {
+            fundsText = fundsText + ", " + "已打款";
+          } else if (item.accountPaymentStatus == "20") {
+            fundsText = fundsText + ", " + "未打款";
+          }
+          return {
+            ...item,
+            currencyTotal: item.currency + " " + item.total,
+            fundsText: fundsText,
+          };
+        });
+      }
+      listData.value = type === "refresh" ? res.data.rows : listData.value.concat(res.data.rows);
+      if (req.value.pageNum * 10 >= res.data.total) {
+        finished.value = true;
+      }
+      req.value.pageNum++;
+      loading.value = false;
+    })
+    .catch(() => {
+      loading.value = false;
+    });
+};
+const toDtl = (row) => {
+  // if (row.status == "10") {
+  //   proxy.$router.push({
+  //     path: "accountPaymentDetail",
+  //     query: {
+  //       id: row.id,
+  //     },
+  //   });
+  // } else if (row.status == "20") {
+  //   proxy.$router.push({
+  //     path: "accountPaymentAdd",
+  //     query: {
+  //       id: row.id,
+  //     },
+  //   });
+  // }
+};
+const listConfig = ref([
+  {
+    label: proxy.t("funds.createTime"),
+    prop: "createTime",
+  },
+  {
+    label: proxy.t("funds.currencyTotal"),
+    prop: "currencyTotal",
+  },
+  {
+    label: proxy.t("funds.paymentRemarks"),
+    prop: "paymentRemarks",
+  },
+  {
+    label: proxy.t("funds.fundsText"),
+    prop: "fundsText",
+  },
+]);
+</script>
+
+<style lang="scss" scoped>
+.list {
+  min-height: 70vh;
+}
+</style>