Procházet zdrojové kódy

feat:添加客户列表逻辑功能

wangsisi před 3 dny
rodič
revize
3284253c92

+ 2 - 0
package.json

@@ -17,6 +17,7 @@
     "@iconify/iconify": "^3.1.0",
     "@iconify/json": "^2.2.42",
     "@iconify/vue": "^4.1.0",
+    "ali-oss": "^6.20.0",
     "@turf/turf": "^7.2.0",
     "@vueuse/core": "^8.7.5",
     "axios": "^0.27.2",
@@ -25,6 +26,7 @@
     "echarts": "^5.4.1",
     "element-plus": "^2.2.8",
     "es6-promise": "^4.2.8",
+    "js-base64": "^3.7.7",
     "highcharts": "^11.0.0",
     "html2canvas": "^1.4.1",
     "jquery": "^3.6.3",

binární
src/assets/images/components/close.png


+ 1 - 0
src/components/chartBox.vue

@@ -82,6 +82,7 @@ const handleShrink = () =>{
     padding: 4px 8px;
     box-sizing: border-box;
     background: #101010;
+    border-radius: 0 0 4px 4px;
     .arrow{
         position: absolute;
         right: -16px;

+ 124 - 0
src/components/common/upload.vue

@@ -0,0 +1,124 @@
+<template>
+    <div class="upload-wrap">
+        <el-upload
+            v-model:file-list="fileList"
+            action="#"
+            :limit="3"
+            :http-request="uploading"
+            :auto-upload="true"
+            list-type="picture-card"
+            class="upload"
+        >
+            <el-icon><Plus /></el-icon>
+            <template #file="{ file }">
+                <div>
+                    <img class="el-upload-list__item-thumbnail" :src="file.url" alt="" />
+                    <span class="el-upload-list__item-actions">
+                        <span class="el-upload-list__item-delete" @click="handleRemove(file)">
+                            <img src="@/assets/images/components/close.png" alt="" />
+                        </span>
+                    </span>
+                </div>
+            </template>
+        </el-upload>
+    </div>
+</template>
+
+<script setup>
+import { onMounted, ref} from "vue";
+import eventBus from "@/api/eventBus";
+import { base_img_url2 } from "@/api/config";
+import { getFileExt } from "@/utils/util";
+import UploadFile from "@/utils/upliadFile";
+// import { useStore } from "vuex";
+
+// const props = defineProps({
+ 
+// })
+
+// const store = useStore();
+// const miniUserId = store.state.home.miniUserId;
+const miniUserId = 81881;
+
+//上传照片
+const fileList = ref([]);
+const imgArr = ref([])
+const uploadFileObj = new UploadFile();
+//上传中
+const uploading = (option) => {
+    let file = option.file;
+    let ext = getFileExt(file.name);
+    let key = `birdseye-look-mini/${miniUserId}/${new Date().getTime()}.${ext}`;
+    uploadFileObj.put(key, file).then((resFilename) => {
+        let index = fileList.value.findIndex((item) => item.name == file.name);
+        fileList.value[index].url = base_img_url2 + resFilename;
+        imgArr.value[index] = resFilename
+        eventBus.emit('upload:change',fileList.value)
+        eventBus.emit('upload:changeArr',imgArr.value)
+    });
+};
+
+//删除图片
+const handleRemove = (uploadFile) => {
+    const index = fileList.value.findIndex((f) => f.uid === uploadFile.uid); // 通过 uid 来识别
+    if (index !== -1) {
+        fileList.value.splice(index, 1);
+        imgArr.value.splice(index, 1);
+        eventBus.emit('upload:change',fileList.value)
+        eventBus.emit('upload:changeArr',imgArr.value)
+    }
+};
+
+function uploadReset(){
+    fileList.value = []
+    imgArr.value = []
+}
+
+onMounted(()=>{
+    eventBus.off('upload:reset',uploadReset)
+    eventBus.on('upload:reset',uploadReset)
+})
+</script>
+
+<style lang="scss" scoped>
+.upload-wrap {
+    ::v-deep {
+        .el-select__wrapper:hover {
+            box-shadow: 0 0 0 1px #dcdfe6 inset;
+        }
+    }
+    .upload {
+        position: relative;
+        .el-upload-list__item-delete {
+            position: absolute;
+            display: block;
+            right: -8px;
+            top: -10px;
+            img {
+                width: 22px;
+            }
+        }
+        .el-upload-list--picture-card .el-upload-list__item-actions {
+            opacity: 1;
+            background-color: transparent;
+        }
+        ::v-deep {
+            .el-upload--picture-card {
+                width: 70px;
+                height: 70px;
+            }
+            .el-upload-list--picture-card .el-upload-list__item {
+                width: 70px;
+                height: 70px;
+                overflow: visible;
+                margin-bottom: 0;
+            }
+            .el-upload-list--picture-card .el-upload-list__item-thumbnail {
+                width: 70px;
+                height: 70px;
+                object-fit: cover;
+            }
+        }
+    }
+}
+</style>

+ 45 - 0
src/utils/upliadFile.js

@@ -0,0 +1,45 @@
+import OSS from "ali-oss"
+import { ElMessage } from "element-plus";
+
+class UploadFile {
+  constructor() {
+    this.bucket = 'birdseye';
+    this.region = 'oss-cn-guangzhou'
+    this.credentials = null;
+    this.ossClient = null
+    this.getCredential()
+  }
+
+  async put(fileName, file){
+    if(!this.credentials){
+      alert("初始化失败!")
+      return;
+    }
+    let result = await this.ossClient.put(fileName, file)
+    return result.name;
+  }
+  //获取授权
+  getCredential() {
+    let that = this;
+    return VE_API.ali.credential()
+        .then( ({data})=> {
+          that.credentials = data;
+          that.initOSSClient()
+        })
+  }
+  //初始化oss对象
+  initOSSClient() {
+    let that = this;
+    const { accessKeyId, accessKeySecret, securityToken } = this.credentials;
+    this.ossClient = new OSS({
+      accessKeyId: accessKeyId,
+      accessKeySecret: accessKeySecret,
+      stsToken: securityToken,
+      bucket: that.bucket,
+      region: that.region
+    });
+  }
+}
+
+
+export default UploadFile;

+ 12 - 0
src/utils/util.js

@@ -53,3 +53,15 @@ export const newAreaFeature = (data)=>{
     }
     return feature;
 }
+
+/**
+ * 获取文件名后缀
+ * @param filePath
+ * @returns {string}
+ */
+ export function getFileExt(filePath) {
+    let index= filePath.lastIndexOf(".");
+//获取后缀
+    let ext = filePath.substring(index+1);
+    return ext;
+}

+ 350 - 23
src/views/home/components/clientList.vue

@@ -6,12 +6,12 @@
                     <el-icon><search /></el-icon>
                 </template>
             </el-input>
-            <div class="button">
+            <div class="button" @click="handlePerson('add')">
                 <img src="@/assets/images/foster-home/firend-icon.png" alt="" />
                 新增客户
             </div>
         </div>
-        <div class="list-content">
+        <div class="list-content" :class="{'max-height':!isManage}">
             <collapse v-model="activeNames">
                 <collapse-item :name="index" v-for="(item, index) in list" :key="index" :is-link="false">
                     <template #title>
@@ -20,15 +20,16 @@
                         <span class="span">{{ item.children.length }}</span>
                     </template>
                     <template #value>
-                        <div @click.stop="hadnleManage(item)" class="text">管理</div>
+                        <div @click.stop="hadnleManage(item,index)" class="text">{{isManage?'管理':'取消管理'}}</div>
                     </template>
-                    <div class="list-item" v-for="(ele, idx) in item.children" :key="idx + ele.id">
+                    <div class="list-item" :class="{active:ele.checked}" v-for="(ele, idx) in item.children" :key="idx + ele.id">
+                      <checkbox class="checkbox" @change="changeCheck" v-show="setingShow" v-model="ele.checked"></checkbox>
                         <div class="item-flex">
                             <img class="photo" src="@/assets/images/foster-home/image.png" alt="" />
                             <div class="item-text">
                                 <div class="name">
                                     <span>{{ ele.name }}</span>
-                                    <el-icon color="#FFD489" size="16" ><Edit /></el-icon>
+                                    <el-icon class="icon" @click.stop="handlePerson('edit')" color="#FFD489" size="16" ><Edit /></el-icon>
                                 </div>
                                 <div>电话:19875236548</div>
                                 <div>地址:湖北省武汉市富里唱鑫园5023</div>
@@ -38,23 +39,87 @@
                 </collapse-item>
             </collapse>
         </div>
-        <!-- <div class="list-footer">
-          <div class="settings">批量设置</div>
-        </div> -->
+        <div class="list-footer">
+          <div class="settings" v-show="!isSetting && isManage" @click="handleGroup">新增分组</div>
+          <div class="settings" v-show="!isSetting && !isManage" @click="handleSetting">批量设置</div>
+          <div class="operation flex" v-show="isSetting && !setingShow">
+            <div class="delete" @click="handleDelete">删除分组</div>
+            <div class="btn-group flex">
+              <div class="add" @click="handleAdd">添加</div>
+              <div class="remove" @click="handleRemove">移除</div>
+            </div>
+          </div>
+          <div class="controls flex" v-show="isSetting && setingShow">
+            <div class="personnel flex">
+              <div class="circle flex" v-show="filterList.length"><el-icon color="#000" size="11"><ArrowUpBold /></el-icon></div>
+              <div class="text van-ellipsis">
+                <template v-for="(item,index) in filterList" :key="index">
+                  {{item.name}}<span v-show="filterList.length - 1 !== index">/</span>
+                </template>
+              </div>
+            </div>
+            <div class="delete" @click="handleGroup" v-show="listType==='other'">设置分组</div>
+            <div class="delete" @click="handleOperation" v-show="listType!=='other'">{{operationType==='add'?'添加':'移出'}}({{filterList.length}})</div>
+          </div>
+        </div>
+    </div>
+  <!-- 添加分组 -->
+  <popup v-model:show="showGroup" closeable round class="popup-custom" :close-on-click-overlay="false">
+    <div class="popup-title">添加分组</div>
+    <div class="popup-content">
+      <el-input class="input" v-model="inputVal" size="large" placeholder="请输入名称" />
     </div>
+    <div class="popup-footer">
+      <div class="cancel" @click="handleCancel">取消</div>
+      <div @click="handleAddGroup">添加</div>
+    </div>
+  </popup>
+  <!-- 新增客户、编辑客户 -->
+  <popup v-model:show="showClient" closeable round class="popup-custom" :close-on-click-overlay="false">
+    <div class="popup-title">{{typePopup==='add'?'新增客户':'编辑客户'}}</div>
+    <div class="popup-content">
+      <el-form
+        ref="ruleFormRef"
+        :model="ruleForm"
+        :rules="rules"
+        label-width="auto"
+        class="rule-form"
+        size="large"
+      >
+        <el-form-item label="姓名" prop="name">
+          <el-input class="input" v-model="ruleForm.name" size="large" placeholder="请输入姓名" />
+        </el-form-item>
+        <el-form-item label="电话" prop="tel">
+          <el-input class="input" v-model="ruleForm.tel" size="large" placeholder="请输入电话号码" />
+        </el-form-item>
+        <el-form-item label="地址" prop="adress">
+          <el-input class="input" v-model="ruleForm.adress" size="large" placeholder="请输入地址" />
+        </el-form-item>
+        <el-form-item label="头像" prop="photo">
+          <!-- <upload></upload> -->
+        </el-form-item>
+      </el-form>
+    </div>
+    <div class="popup-footer">
+      <div class="cancel" @click="resetForm">取消</div>
+      <div @click="submitForm">添加</div>
+    </div>
+  </popup>
 </template>
 
 <script setup>
-import { ref } from "vue";
-import { Collapse, CollapseItem, Popup } from "vant";
+import { ref,reactive } from "vue";
+import { Collapse, CollapseItem,Checkbox,Popup,showConfirmDialog } from "vant";
 import { useRouter } from "vue-router";
 import { ElMessage } from 'element-plus'
+import { deepClone } from "@/common/commonFun";
+import upload from "@/components/common/upload.vue";
 const router = useRouter();
 
 //新建分组
 const showGroupPopup = ref(false);
 const showPopup = () => {
-    showGroupPopup.value = true;
+  showGroupPopup.value = true;
 };
 const input = ref("");
 const distributionShow = ref(false);
@@ -62,16 +127,148 @@ const distributionShow = ref(false);
 // 果树选中的监听事件
 const checkedList = ref([])
 const changeActive = (arr) =>{
-    checkedList.value = arr
+  checkedList.value = arr
 }
 
+const isManage = ref(true)
+// const curIndex = ref(0)
+const listType = ref('')
 // 管理
-const hadnleManage = (value) => {
-  console.log('val',value);
-  list.value = [defalutList[1]]
+const hadnleManage = (value,index) => {
+  // console.log('val',value);
+  listType.value = 'often'
+  isSetting.value = true
+  if(value.name==='未分组列表'){
+    listType.value = 'other'
+    isSetting.value = false
+  }
+  // curIndex.value = index
+  isManage.value = !isManage.value
+  if(!isManage.value){
+    list.value = deepClone([defalutList.value[index]])
+  }else{
+    resetList()
+  }
 };
 
-const defalutList = [
+const resetList = () =>{
+  
+  list.value = deepClone(defalutList.value)
+  setingShow.value = false
+  isSetting.value = false
+  isManage.value = true
+}
+
+const isSetting = ref(false)
+//设置
+const handleSetting = () =>{
+  isSetting.value = true
+  if(listType.value==='other'){
+    setingShow.value = true
+  }
+}
+
+const operationType = ref('')
+//添加
+const handleAdd = () =>{
+  operationType.value = 'add'
+  setingShow.value = true
+}
+
+//移除
+const setingShow = ref(false)
+const handleRemove = () =>{
+  operationType.value = 'remove'
+  setingShow.value = true
+}
+
+const filterList = ref([]);
+const changeCheck = () =>{
+  filterList.value = list.value[0].children.filter((item) => item.checked);
+}
+
+//设置分组
+const showGroup = ref(false)
+const inputVal = ref('')
+const handleGroup = () =>{
+  // if(filterList.value.length<1) return ElMessage.warning('请选择用户')
+  showGroup.value = true
+}
+
+//添加分组
+const handleAddGroup = () =>{
+  if(inputVal.value.length<1) return ElMessage.warning('请输入名称')
+  handleCancel()
+  ElMessage.success('添加成功')
+}
+const handleCancel = () =>{
+  showGroup.value = false
+  inputVal.value = ''
+}
+
+//删除分组
+const handleDelete = () =>{
+  showConfirmDialog({
+    title: '提示',
+    message:'是否删除该分组!',
+  })
+  .then(() => {
+    // on confirm
+  })
+  .catch(() => {
+    // on cancel
+  });
+}
+
+//新增客户
+const typePopup = ref('')
+const handlePerson = (type) =>{
+  typePopup.value = type
+  showClient.value = true
+}
+
+//移出、添加
+const handleOperation = () =>{
+  if(filterList.value.length<1) return ElMessage.warning('请选择用户')
+  if(operationType.value === 'add'){
+    ElMessage.success('添加成功')
+  }else{
+    ElMessage.success('移出成功')
+  }
+  resetList()
+}
+
+const showClient = ref(false)
+const ruleForm = reactive({
+  name:'',
+  tel:'',
+  adress:''
+})
+const rules = reactive({
+  name: { required: true, message: '请输入用户名称', trigger: ['blur','change'] },
+  tel: { required: true, message: '请输入电话号码', trigger: ['blur','change'] },
+})
+
+const ruleFormRef = ref(null)
+const submitForm = async () =>{
+  if (!ruleFormRef.value) return
+  await ruleFormRef.value.validate((valid, fields) => {
+    if (valid) {
+      ElMessage.success('添加成功')
+      showClient.value = false
+    } else {
+      console.log('error submit!')
+    }
+  })
+}
+
+const resetForm = () =>{
+  if (!ruleFormRef.value) return
+  ruleFormRef.value.resetFields()
+  showClient.value = false
+}
+
+const defalutList = ref([
     {
         name: "常用列表",
         id: "1",
@@ -80,10 +277,12 @@ const defalutList = [
             {
                 id: "3",
                 name: "周浩",
+                checked: false,
             },
             {
                 id: "4",
                 name: "王丽丽",
+                checked: false,
             },
         ],
     },
@@ -95,17 +294,20 @@ const defalutList = [
             {
                 id: "5",
                 name: "李莉",
+                checked: false,
             },
             {
                 id: "6",
                 name: "陈林",
+                checked: false,
             },
         ],
     },
-]
-const list = ref(defalutList);
+])
+const list = ref(deepClone(defalutList.value));
 
 const activeNames = ref([0]);
+
 </script>
 
 <style lang="scss" scoped>
@@ -142,6 +344,7 @@ const activeNames = ref([0]);
             justify-content: center;
             padding: 8px;
             border-radius: 5px;
+            cursor: pointer;
             img {
                 width: 20px;
                 height: 17px;
@@ -151,8 +354,11 @@ const activeNames = ref([0]);
     }
     .list-content {
         width: 100%;
-        height: calc(100% - 50px - 40px);
+        height: calc(100% - 50px);
         margin-top: 12px;
+        &.max-height{
+          height: calc(100% - 50px - 55px);
+        }
         .text {
             color: #FFD489;
             cursor: pointer;
@@ -184,7 +390,8 @@ const activeNames = ref([0]);
                 }
             }
             .van-collapse-item__content{
-              background: rgba(255, 255, 255, 0.08);
+              background: transparent;
+              padding: 10px 0 0 0;
             }
             .van-collapse-item__title--expanded {
                 .van-cell__title {
@@ -196,13 +403,28 @@ const activeNames = ref([0]);
             .van-collapse-item + .van-collapse-item {
                 margin-top: 12px;
             }
+
+            .van-checkbox__icon--checked .van-icon{
+              background-color: #F7BE5A;
+              border-color: #F7BE5A;
+              color: #000;
+            }
         }
         .list-item {
-            border-radius: 12px;
+            border-radius: 6px;
             position: relative;
             display: flex;
-            justify-content: space-between;
             align-items: center;
+            background: rgba(255, 255, 255, 0.08);
+            border: 1px solid transparent;
+            padding: 8px 10px;
+            &.active{
+              background: rgba(243, 193, 29, 0.1);
+              border-color: #FFD489;
+            }
+            .checkbox{
+              margin-right: 12px;
+            }
             .item-flex {
                 display: flex;
                 align-items: center;
@@ -226,6 +448,9 @@ const activeNames = ref([0]);
                         font-weight: 500;
                         margin-right: 5px;
                     }
+                    .icon{
+                      cursor: pointer;
+                    }
                 }
             }
         }
@@ -234,16 +459,118 @@ const activeNames = ref([0]);
         }
     }
     .list-footer{
+      position: absolute;
+      bottom: 0;
+      left: 0;
       width: 100%;
       background: rgba(255, 255, 255, 0.08);
       display: flex;
       justify-content: center;
+      box-sizing: border-box;
+      padding: 11px;
       .settings{
         background: rgba(255, 255, 255, 0.1);
         border-radius: 4px;
-        padding: 12px 36px;
+        padding: 8px 36px;
         color: #fff;
+        cursor: pointer;
       }
+      .flex{
+        display: flex;
+        align-items: center;
+      }
+      .delete{
+        border: 1px solid #FFD489;
+        border-radius: 4px;
+        padding: 8px 19px;
+        color: #FFD489;
+        cursor: pointer;
+      }
+      .operation{
+        width: 100%;
+        justify-content: space-between;
+        .btn-group{
+          div{
+            background: rgba(255, 255, 255, 0.1);
+            padding: 8px 19px;
+            border-radius: 4px;
+            cursor: pointer;
+          }
+          .add{
+            margin-right: 10px;
+          }
+        }
+      }
+      .controls{
+        width: 100%;
+        justify-content: space-between;
+        .personnel{
+          width: calc(100% - 100px);
+          .circle{
+            width: 16px;
+            height: 16px;
+            background: #FFD489;
+            border-radius: 50%;
+            justify-content: center;
+            margin-right: 8px;
+          }
+          .text{
+            width: 90%;
+          }
+        }
+        .delete{
+          background: #FFD489;
+          color: #000;
+          width: 85px;
+          box-sizing: border-box;
+          text-align: center;
+          padding: 8px;
+        }
+      }
+    }
+}
+.popup-custom{
+  width: 24%;
+  padding: 31px 25px;
+  ::v-deep{
+    .van-popup__close-icon{
+      color: #000;
+    }
+  }
+  .popup-title{
+    text-align: center;
+    color: #000;
+    font-size: 24px;
+    margin-bottom: 15px;
+  }
+  .popup-content{
+    width: 100%;
+    .input{
+      height: 46px;
+      font-size: 16px;
+    }
+  }
+  .popup-footer{
+    width: 100%;
+    display: flex;
+    border-top: 1px solid rgba(0, 0, 0,0.1);
+    margin-top: 30px;
+    padding-top: 25px;
+    div{
+      flex: 1;
+      background: linear-gradient(120deg,#FFD887,#ED9E1E);
+      border-radius: 6px;
+      padding: 13px;
+      font-size: 20px;
+      color: #fff;
+      text-align: center;
+      cursor: pointer;
+    }
+    .cancel{
+      color: #000;
+      background: #F3F3F3;
+      margin-right: 30px;
     }
+  }
 }
 </style>

+ 1 - 0
src/views/home/homeMap.vue

@@ -60,6 +60,7 @@ const initMap = async () => {
                         getTileUrl: function(x, y, z) {
                             // 天地图影像 WMTS 地址
                             const s = Math.floor(Math.random() * 8); // 随机选择服务器节点
+                            // return `https://t{0-7}.tianditu.gov.cn/img_c/w mts?tk=e95115c454a663cd052d96019fd83840`
                             return `https://t${s}.tianditu.gov.cn/img_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=img&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX=${z}&TILEROW=${y}&TILECOL=${x}&tk=e95115c454a663cd052d96019fd83840`;
                         },
                     }),

+ 1 - 1
src/views/more/medalPage.vue

@@ -40,7 +40,7 @@ const list = ref([
     },
 ]);
 
-const showPopup = ref(true);
+const showPopup = ref(false);
 const handleMedal = () => {
     showPopup.value = true;
 };

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 414 - 279
yarn.lock


Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů