yangfeng
2023-07-14 22d4441a8c669185c13d2b6856c51ad550c50ae2
编辑下拉框&网络请求封装优化
3个文件已删除
8个文件已添加
16个文件已修改
8470 ■■■■■ 已修改文件
package-lock.json 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package.json 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
public/serverconfig.js 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/custom/followupRecords.js 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/custom/salesLead.js 43 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/index.js 84 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/login/login.js 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/responseHandler.js 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/service.js 121 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/tools.js 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/style/reset-element.scss 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/common/config/index.js 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/common/untils/request.js 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/makepager/CommonSelectView.vue 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/makepager/PublicFunctionBtnView.vue 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/index.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/backgroundConfig/memberManage/index.vue 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/custom/followupRecords/index.vue 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/custom/salesLead/AddSalesLeadDialog.vue 57 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/custom/salesLead/index.vue 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/other/commonDialog/EditDropdownDialog.vue 181 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/other/commonDialog/HighViewScopeDialog.vue 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/other/commonDialog/ImportFileDialog.vue 125 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/other/login/index.vue 49 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vue.config.js 42 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vue.develop.config.js 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yarn.lock 7347 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package-lock.json
@@ -11,7 +11,9 @@
        "axios": "^1.4.0",
        "core-js": "^3.8.3",
        "crypto-js": "^4.1.1",
        "dayjs": "^1.11.9",
        "element-ui": "^2.15.13",
        "lodash": "^4.17.19",
        "qs": "^6.11.2",
        "save": "^2.9.0",
        "svg-sprite-loader": "^6.0.11",
@@ -19,7 +21,8 @@
        "vue-i18n": "^8.26.7",
        "vue-router": "^3.0.7",
        "vuex": "^3.6.2",
        "web-storage-cache": "^1.1.1"
        "web-storage-cache": "^1.1.1",
        "webpack-theme-color-replacer": "^1.4.7"
      },
      "devDependencies": {
        "@babel/core": "^7.12.16",
@@ -27,6 +30,7 @@
        "@vue/cli-plugin-babel": "~5.0.0",
        "@vue/cli-plugin-eslint": "~5.0.0",
        "@vue/cli-service": "~5.0.0",
        "compression-webpack-plugin": "^10.0.0",
        "eslint": "^7.32.0",
        "eslint-plugin-vue": "^8.0.3",
        "sass": "^1.63.6",
@@ -4346,6 +4350,67 @@
        "node": ">= 0.8.0"
      }
    },
    "node_modules/compression-webpack-plugin": {
      "version": "10.0.0",
      "resolved": "https://registry.npmmirror.com/compression-webpack-plugin/-/compression-webpack-plugin-10.0.0.tgz",
      "integrity": "sha512-wLXLIBwpul/ALcm7Aj+69X0pYT3BYt6DdPn3qrgBIh9YejV9Bju9ShhlAsjujLyWMo6SAweFIWaUoFmXZNuNrg==",
      "dev": true,
      "dependencies": {
        "schema-utils": "^4.0.0",
        "serialize-javascript": "^6.0.0"
      },
      "engines": {
        "node": ">= 14.15.0"
      },
      "peerDependencies": {
        "webpack": "^5.1.0"
      }
    },
    "node_modules/compression-webpack-plugin/node_modules/ajv": {
      "version": "8.12.0",
      "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.12.0.tgz",
      "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
      "dev": true,
      "dependencies": {
        "fast-deep-equal": "^3.1.1",
        "json-schema-traverse": "^1.0.0",
        "require-from-string": "^2.0.2",
        "uri-js": "^4.2.2"
      }
    },
    "node_modules/compression-webpack-plugin/node_modules/ajv-keywords": {
      "version": "5.1.0",
      "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
      "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
      "dev": true,
      "dependencies": {
        "fast-deep-equal": "^3.1.3"
      },
      "peerDependencies": {
        "ajv": "^8.8.2"
      }
    },
    "node_modules/compression-webpack-plugin/node_modules/json-schema-traverse": {
      "version": "1.0.0",
      "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
      "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
      "dev": true
    },
    "node_modules/compression-webpack-plugin/node_modules/schema-utils": {
      "version": "4.2.0",
      "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-4.2.0.tgz",
      "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==",
      "dev": true,
      "dependencies": {
        "@types/json-schema": "^7.0.9",
        "ajv": "^8.9.0",
        "ajv-formats": "^2.1.1",
        "ajv-keywords": "^5.1.0"
      },
      "engines": {
        "node": ">= 12.13.0"
      }
    },
    "node_modules/compression/node_modules/debug": {
      "version": "2.6.9",
      "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz",
@@ -4827,6 +4892,11 @@
      "version": "3.1.2",
      "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.2.tgz",
      "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ=="
    },
    "node_modules/dayjs": {
      "version": "1.11.9",
      "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.9.tgz",
      "integrity": "sha512-QvzAURSbQ0pKdIye2txOzNaHmxtUBXerpY0FJsFXUMKbIZeFm5ht1LS/jFsrncjnmtv8HsG0W2g6c0zUjZWmpA=="
    },
    "node_modules/de-indent": {
      "version": "1.0.2",
@@ -7580,8 +7650,7 @@
    "node_modules/lodash": {
      "version": "4.17.21",
      "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
      "dev": true
      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
    },
    "node_modules/lodash.assign": {
      "version": "4.2.0",
@@ -12702,6 +12771,11 @@
        "node": ">=10.13.0"
      }
    },
    "node_modules/webpack-theme-color-replacer": {
      "version": "1.4.7",
      "resolved": "https://registry.npmmirror.com/webpack-theme-color-replacer/-/webpack-theme-color-replacer-1.4.7.tgz",
      "integrity": "sha512-q3zm8/z+Kli7oWov3/glL/ts6dOVuqjQG8aeEzXOgwrKdvFOCemXn8ipq2za/I9LfdwSRlsEDcLNS6P6vKlOxg=="
    },
    "node_modules/webpack-virtual-modules": {
      "version": "0.4.6",
      "resolved": "https://registry.npmmirror.com/webpack-virtual-modules/-/webpack-virtual-modules-0.4.6.tgz",
package.json
@@ -11,7 +11,9 @@
    "axios": "^1.4.0",
    "core-js": "^3.8.3",
    "crypto-js": "^4.1.1",
    "dayjs": "^1.11.9",
    "element-ui": "^2.15.13",
    "lodash": "^4.17.19",
    "qs": "^6.11.2",
    "save": "^2.9.0",
    "svg-sprite-loader": "^6.0.11",
@@ -19,7 +21,8 @@
    "vue-i18n": "^8.26.7",
    "vue-router": "^3.0.7",
    "vuex": "^3.6.2",
    "web-storage-cache": "^1.1.1"
    "web-storage-cache": "^1.1.1",
    "webpack-theme-color-replacer": "^1.4.7"
  },
  "devDependencies": {
    "@babel/core": "^7.12.16",
@@ -27,6 +30,7 @@
    "@vue/cli-plugin-babel": "~5.0.0",
    "@vue/cli-plugin-eslint": "~5.0.0",
    "@vue/cli-service": "~5.0.0",
    "compression-webpack-plugin": "^10.0.0",
    "eslint": "^7.32.0",
    "eslint-plugin-vue": "^8.0.3",
    "sass": "^1.63.6",
public/serverconfig.js
File was deleted
src/api/custom/followupRecords.js
New file
@@ -0,0 +1,9 @@
import request from "@/common/untils/request.js"
// 跟进记录列表
export function getFollowRecordList() {
  return request({
    url: "/api/followRecord/list",
    method: "get"
  })
}
src/api/custom/salesLead.js
@@ -1,9 +1,38 @@
import { POST } from "@/api/index"
import { manageContextPath } from "@/common/config"
import request from "@/common/untils/request.js"
export default {
  // 销售线索列表
  getSalesLeadList: (params) => {
    return POST(manageContextPath + "", { params })
  }
// 销售线索列表
export function getSalesLeadsList() {
  return request({
    url: "/api/salesLeads/list",
    method: "get"
  })
}
// 添加销售线索
export function getAddSalesLeads() {
  return request({
    url: "/api/salesLeads/add",
    method: "post"
  })
}
// 删除销售线索
export function getDeleteSalesLeads(data) {
  return request({
    url: "/api/salesLeads/delete",
    method: "delete",
    data
  })
}
// 更新销售线索
export function getUpdateSalesLeads() {
  return request({
    url: "/api/salesLeads/update",
    method: "put"
  })
}
// 商机来源列表
export function getSalesSourcesList() {
  return request({
    url: "/api/salesSources/list",
    method: "get"
  })
}
src/api/index.js
@@ -1,71 +1,19 @@
import ax from "axios"
import { IS_PRODUCTION } from "@/common/config"
// import { wsCache } from "@/common/untils/wsCache"
import { responseHandler, errorResponseHandler } from "./responseHandler"
import { assign, map } from "lodash"
import faker from "faker/locale/zh_CN"
import { service, request } from "./service"
import * as tools from "./tools"
const JsonText = window.getServerJson
const axios = ax.create({
  baseURL: IS_PRODUCTION ? JsonText.managePath : "", // url = base url + request url
  withCredentials: true,
  headers: {
    "X-Requested-With": "XMLHttpRequest", // 给后台区分异步请求
    "Content-Type": "application/json;charset=UTF-8"
  },
  timeout: 300000 // request timeout
})
// 添加一个请求拦截器
axios.interceptors.request.use(
  (request) => {
    if (request.data instanceof FormData) {
      // 不处理
    } else if (request.data) {
      request.data["domain"] = JsonText.systemCode
    } else {
      request.data = { domain: JsonText.systemCode }
    }
const files = require.context("./modules", true, /\.api\.js$/)
const generators = files.keys().map((key) => files(key).default)
    //用户登录标记
    // const token = wsCache.get('token')
    // request.headers['token'] = token
    return request
  },
  (error) => {
    return Promise.reject(error)
  }
export default assign(
  {},
  ...map(generators, (generator) =>
    generator({
      service,
      request,
      faker,
      tools
    })
  )
)
// 添加一个响应拦截器
axios.interceptors.response.use(
  (response) => {
    // 处理响应请求池
    if (response.data.status.toString() === "200" && response.data.success === true) {
      return response
    } else {
      responseHandler(response)
      return Promise.reject(response.data)
    }
  },
  (error) => {
    errorResponseHandler(error)
    return Promise.reject(error)
  }
)
// 通用方法
export const POST = (url, params, config = {}) => {
  return axios.post(`${url}`, params, config).then((res) => res.data)
}
export const GET = (url, params, config = {}) => {
  return axios.get(`${url}`, { params: params, ...config }).then((res) => res.data)
}
export const PUT = (url, params, config = {}) => {
  return axios.put(`${url}`, params, config).then((res) => res.data)
}
export const DELETE = (url, params, config = {}) => {
  return axios.delete(`${url}`, { params: params }, config).then((res) => res.data)
}
export const PATCH = (url, params, config = {}) => {
  return axios.patch(`${url}`, params, config).then((res) => res.data)
}
export const Axios = axios
src/api/login/login.js
New file
@@ -0,0 +1,18 @@
import request from "@/common/untils/request.js"
// 获取验证码
export function getCaptcha() {
  return request({
    url: "/api/base/captcha",
    method: "post"
  })
}
// 登录
export function login(data) {
  return request({
    url: "/api/base/login",
    method: "post",
    data
  })
}
src/api/responseHandler.js
File was deleted
src/api/service.js
New file
@@ -0,0 +1,121 @@
import axios from "axios"
import { get } from "lodash"
import util from "@/libs/util"
import { errorLog, errorCreate } from "./tools"
/**
 * @description 创建请求实例
 */
function createService() {
  // 创建一个 axios 实例
  const service = axios.create()
  // 请求拦截
  service.interceptors.request.use(
    (config) => config,
    (error) => {
      // 发送失败
      console.log(error)
      return Promise.reject(error)
    }
  )
  // 响应拦截
  service.interceptors.response.use(
    (response) => {
      // dataAxios 是 axios 返回数据中的 data
      const dataAxios = response.data
      // 这个状态码是和后端约定的
      const { code } = dataAxios
      // 根据 code 进行判断
      if (code === undefined) {
        // 如果没有 code 代表这不是项目后端开发的接口 比如可能是 D2Admin 请求最新版本
        return dataAxios
      } else {
        // 有 code 代表这是一个后端接口 可以进行进一步的判断
        switch (code) {
          case 0:
            // [ 示例 ] code === 0 代表没有错误
            return dataAxios.data
          case "xxx":
            // [ 示例 ] 其它和后台约定的 code
            errorCreate(`[ code: xxx ] ${dataAxios.msg}: ${response.config.url}`)
            break
          default:
            // 不是正确的 code
            errorCreate(`${dataAxios.msg}: ${response.config.url}`)
            break
        }
      }
    },
    (error) => {
      const status = get(error, "response.status")
      switch (status) {
        case 400:
          error.message = "请求错误"
          break
        case 401:
          error.message = "未授权,请登录"
          break
        case 403:
          error.message = "拒绝访问"
          break
        case 404:
          error.message = `请求地址出错: ${error.response.config.url}`
          break
        case 408:
          error.message = "请求超时"
          break
        case 500:
          error.message = "服务器内部错误"
          break
        case 501:
          error.message = "服务未实现"
          break
        case 502:
          error.message = "网关错误"
          break
        case 503:
          error.message = "服务不可用"
          break
        case 504:
          error.message = "网关超时"
          break
        case 505:
          error.message = "HTTP版本不受支持"
          break
        default:
          break
      }
      errorLog(error)
      return Promise.reject(error)
    }
  )
  return service
}
/**
 * @description 创建请求方法
 * @param {Object} service axios 实例
 */
function createRequestFunction(service) {
  return function (config) {
    const token = util.cookies.get("token")
    const configDefault = {
      headers: {
        Authorization: token,
        "Content-Type": get(config, "headers.Content-Type", "application/json")
      },
      timeout: 5000,
      baseURL: process.env.VUE_APP_API,
      data: {}
    }
    return service(Object.assign(configDefault, config))
  }
}
// 用于真实网络请求的实例和请求方法
export const service = createService()
export const request = createRequestFunction(service)
// 用于模拟网络请求的实例和请求方法
export const serviceForMock = createService()
export const requestForMock = createRequestFunction(serviceForMock)
src/api/tools.js
New file
@@ -0,0 +1,72 @@
import { Message } from "element-ui"
/**
 * @description 安全地解析 json 字符串
 * @param {String} jsonString 需要解析的 json 字符串
 * @param {String} defaultValue 默认值
 */
export function parse(jsonString = "{}", defaultValue = {}) {
  let result = defaultValue
  try {
    result = JSON.parse(jsonString)
  } catch (error) {
    console.log(error)
  }
  return result
}
/**
 * @description 接口请求返回
 * @param {Any} data 返回值
 * @param {String} msg 状态信息
 * @param {Number} code 状态码
 */
export function response(data = {}, msg = "", code = 0) {
  return [200, { code, msg, data }]
}
/**
 * @description 接口请求返回 正确返回
 * @param {Any} data 返回值
 * @param {String} msg 状态信息
 */
export function responseSuccess(data = {}, msg = "成功") {
  return response(data, msg)
}
/**
 * @description 接口请求返回 错误返回
 * @param {Any} data 返回值
 * @param {String} msg 状态信息
 * @param {Number} code 状态码
 */
export function responseError(data = {}, msg = "请求失败", code = 500) {
  return response(data, msg, code)
}
/**
 * @description 记录和显示错误
 * @param {Error} error 错误对象
 */
export function errorLog(error) {
  // 打印到控制台
  if (process.env.NODE_ENV === "development") {
    console.log(error)
  }
  // 显示提示
  Message({
    message: error.message,
    type: "error",
    duration: 5 * 1000
  })
}
/**
 * @description 创建一个错误
 * @param {String} msg 错误信息
 */
export function errorCreate(msg) {
  const error = new Error(msg)
  errorLog(error)
  throw error
}
src/assets/style/reset-element.scss
@@ -12,6 +12,9 @@
  padding: 0px;
}
.el-dialog__footer {
  height: 55px;
  line-height: 55px;
  background-color: #f5f5f5;
  padding: 0px;
  text-align: center;
  box-sizing: border-box;
src/common/config/index.js
@@ -1,14 +0,0 @@
let isProduction = process.env.NODE_ENV === "production"
let configObj = window.getServerJson
// 生产环境请求地址
export const baseUrl = configObj.managePath
// 是否是生产环境,打包的时候值会是true, 开发的时候值是false
export const IS_PRODUCTION = isProduction
// @/common/until/wsCache 加密使用的盐
export const LOCAL_ENCRYPTED_KEY = configObj.localEncryptedKey
// 接口上下文
export const manageContextPath = configObj.manageContextPath
src/common/untils/request.js
New file
@@ -0,0 +1,60 @@
import axios from "axios"
import { Message } from "element-ui"
// import router from '@/router'
const Axios = axios.create({
  responseType: "json",
  withCredentials: true // 是否允许带cookie这些
})
/* //POST传参序列化(添加请求拦截器) */
Axios.interceptors.request.use(
  (config) => {
    // 若是有做鉴权token , 就给头部带上token
    // let token = util.cookies.get("token");
    // if (token != undefined) {
    //   config.headers.Authorization = "Bearer " + token;
    // }
    return config
  },
  (error) => {
    return Promise.reject(error)
  }
)
/* //返回状态判断(添加响应拦截器) */
Axios.interceptors.response.use(
  (res) => {
    /* //对响应数据做些事 */
    if (res.data.code === 200) {
      return res.data ? res.data : {}
    } else {
      Message({
        message: res.data.msg,
        type: "error",
        duration: 5 * 1000
      })
      return Promise.reject(res.data)
    }
  },
  (error) => {
    let { message } = error
    if (message === "Network Error") {
      message = "后端接口连接异常"
    } else if (message.includes("timeout")) {
      message = "系统接口请求超时"
    } else if (message.includes("Request failed with status code")) {
      message = "系统接口" + message.substr(message.length - 3) + "异常"
    }
    Message({
      message: message,
      type: "error",
      duration: 5 * 1000
    })
    return Promise.reject(error)
  }
)
export default Axios
src/components/makepager/CommonSelectView.vue
@@ -3,7 +3,7 @@
    <el-select v-model="commonDefautValue" placeholder="请选择" class="common-select-sel" size="mini">
      <el-option v-for="item in commonOptions" :key="item.value" :label="item.label" :value="item.value"> </el-option>
    </el-select>
    <div class="common-select-btn">
    <div class="common-select-btn" @click="commonSelClick">
      <i class="el-icon-setting"></i>
    </div>
  </div>
@@ -27,7 +27,11 @@
      commonDefautValue: this.commonValue
    }
  },
  methods: {}
  methods: {
    commonSelClick() {
      this.$emit("editDropdownBox")
    }
  }
}
</script>
@@ -41,6 +45,7 @@
  .common-select-btn {
    margin-left: 5px;
    font-size: 16px;
    cursor: pointer;
  }
}
</style>
src/components/makepager/PublicFunctionBtnView.vue
@@ -161,7 +161,9 @@
    // 查重
    duplicateCheckBtnClick() {},
    // 导入
    importBtnClick() {},
    importBtnClick() {
      this.$parent.importClitk()
    },
    // 部门设置
    departmentSetClick() {},
    // 数据范围
src/router/index.js
@@ -8,7 +8,7 @@
import backgroundConfigRouter from "./backgroundConfig/index.js"
Vue.use(Router)
const login = (resolve) => require(["@/views/login/index"], resolve)
const login = (resolve) => require(["@/views/other/login/index"], resolve)
const custom = (resolve) => require(["@/views/custom/index"], resolve)
const sales = (resolve) => require(["@/views/sales/index"], resolve)
const service = (resolve) => require(["@/views/service/index"], resolve)
src/views/backgroundConfig/memberManage/index.vue
@@ -237,14 +237,15 @@
    // 编辑部门
    editDepartmentClick(command, data) {
      console.log(command, data)
      this.departmentConfig.visible = true
      if (command === "edit") {
        this.departmentConfig.visible = true
        this.departmentConfig.infomation = {
          departmentName: data.label,
          departmentNumber: data.id,
          notes: ""
        }
      } else if (command === "add") {
        this.departmentConfig.visible = true
        this.departmentConfig.infomation = {
          departmentName: "",
          departmentNumber: "",
src/views/custom/followupRecords/index.vue
@@ -21,6 +21,8 @@
<script>
import AddFollowupRecordsDialog from "@/views/custom/followupRecords/AddFollowupRecordsDialog"
import { getFollowRecordList } from "@/api/custom/followupRecords.js"
export default {
  name: "FollowupRecords",
  props: {},
@@ -62,6 +64,7 @@
  },
  created() {
    this.setTable()
    this.getData()
  },
  methods: {
    setTable() {
@@ -97,6 +100,16 @@
        this.searchOptions.push({ value: (i + 1).toString(), label: label })
      }
    },
    // 请求数据
    async getData() {
      await getFollowRecordList()
        .then((res) => {
          console.log("成功" + res)
        })
        .catch((err) => {
          console.log(err)
        })
    },
    // 新建
    addBtnClick() {
      this.editConfig.visible = true
src/views/custom/salesLead/AddSalesLeadDialog.vue
@@ -27,7 +27,7 @@
              </el-col>
              <el-col :span="12">
                <el-form-item label="销售线索编号" prop="saleLeadNumber">
                  <span>{{ editSalesLeadConfig.infomationsaleLeadNumber }}</span>
                  <el-input v-model="editConfig.infomation.saleLeadNumber"></el-input>
                </el-form-item>
              </el-col>
            </el-row>
@@ -61,6 +61,7 @@
                  <CommonSelectView
                    :common-value="editConfig.infomation.businessSource"
                    :common-options="businessSourceOptions"
                    @editDropdownBox="editDropdownBox"
                  />
                </el-form-item>
              </el-col>
@@ -152,16 +153,21 @@
          <div v-else><i class="el-icon-arrow-down"></i></div>
        </div>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button type="primary" size="small" @click="editConfig.visible = false">保 存</el-button>
      <div slot="footer">
        <el-button type="primary" size="small" @click="saveClick('form')">保 存</el-button>
        <el-button size="small" @click="editConfig.visible = false">取 消</el-button>
      </div>
      <!-- 编辑下拉框 -->
      <EditDropdownDialog v-if="editDropdownConfig.editVisible" :edit-dropdown-config="editDropdownConfig" />
    </el-dialog>
  </div>
</template>
<script>
import CommonSelectView from "@/components/makepager/CommonSelectView"
import EditDropdownDialog from "@/views/other/commonDialog/EditDropdownDialog"
import { getSalesSourcesList } from "@/api/custom/salesLead"
export default {
  name: "AddSalesLeadDialog",
  props: {
@@ -192,7 +198,7 @@
      }
    }
  },
  components: { CommonSelectView },
  components: { CommonSelectView, EditDropdownDialog },
  computed: {
    searchCommonHeight() {
      return this.$refs.searchCommonView.offsetHeight
@@ -247,13 +253,41 @@
        { value: "4", label: "西城区" }
      ], // 区域
      unflodCollapseStr: "收起",
      isUnflod: true
      isUnflod: true,
      editDropdownConfig: {
        editVisible: false,
        title: "",
        infomation: {}
      }
    }
  },
  created() {},
  created() {
    this.getCommonData()
  },
  methods: {
    getCommonData() {
      getSalesSourcesList()
        .then((res) => {
          console.log(res)
          // this.businessSourceOptions = res.data.tableList
        })
        .catch((err) => {
          console.log(err)
        })
    },
    handleClose() {
      this.editConfig.visible = false
    },
    // 保存
    saveClick(formName) {
      this.$refs[formName].validate((valid) => {
        if (valid) {
          alert("submit")
        } else {
          console.log("error submit")
          return false
        }
      })
    },
    // 展开收起
    unflodCollapseClick() {
@@ -264,6 +298,12 @@
        this.unflodCollapseStr = "收起"
        this.isUnflod = true
      }
    },
    // 编辑下拉框
    editDropdownBox() {
      console.log("aaa")
      this.editDropdownConfig.editVisible = true
      this.editDropdownConfig.title = "商机来源"
    }
  }
}
@@ -297,11 +337,6 @@
    justify-content: center;
    align-items: center;
    color: #6166d3;
  }
  .dialog-footer {
    background-color: #f5f5f5;
    height: 55px;
    line-height: 55px;
  }
}
</style>
src/views/custom/salesLead/index.vue
@@ -18,18 +18,23 @@
    </TableCommonView>
    <!-- 新建/编辑销售线索 -->
    <AddSalesLeadDialog v-if="editSalesLeadConfig.visible" :edit-sales-lead-config="editSalesLeadConfig" />
    <!-- 导入文件 -->
    <ImportFileDialog v-if="importConfig.visible" :import-file-config="importConfig" />
  </div>
</template>
<script>
import AddSalesLeadDialog from "@/views/custom/salesLead/AddSalesLeadDialog"
import pageMixin from "@/components/makepager/pager/mixin/pageMixin"
import ImportFileDialog from "@/views/other/commonDialog/ImportFileDialog"
import { getSalesLeadsList } from "@/api/custom/salesLead"
export default {
  name: "SalesLead",
  props: {},
  components: {
    AddSalesLeadDialog
    AddSalesLeadDialog,
    ImportFileDialog
  },
  mixins: [pageMixin],
  computed: {},
@@ -57,6 +62,11 @@
        visible: false,
        title: "新建",
        infomation: {}
      },
      importConfig: {
        visible: false,
        title: "新建",
        infomation: {}
      }
    }
  },
@@ -67,24 +77,13 @@
  methods: {
    setTable() {
      this.tableList = {
        tableInfomation: [
          {
            customName: "上海通用机械有限公司",
            saleLeadNumber: "LEA110",
            contactName: "董奇伟",
            phoneNumber: "15988887777",
            businessSource: "朋友介绍",
            province: "江苏省",
            city: "连云港市",
            owner: "销售总监"
          }
        ],
        tableInfomation: [],
        tableColumn: [
          { label: "客户名称", prop: "customName", min: 190 }, // 客户名称
          { label: "销售线索编号", prop: "saleLeadNumber", min: 190 }, // 销售线索编号
          { label: "联系人姓名", prop: "contactName", min: 190 }, // 联系人姓名
          { label: "手机号码", prop: "phoneNumber", min: 190 }, // 手机号码
          { label: "商机来源", prop: "businessSource", min: 190 }, // 商机来源
          { label: "客户名称", prop: "name", min: 190 }, // 客户名称
          { label: "销售线索编号", prop: "number", min: 190 }, // 销售线索编号
          { label: "联系人姓名", prop: "contact_name", min: 190 }, // 联系人姓名
          { label: "手机号码", prop: "contact_phone", min: 190 }, // 手机号码
          { label: "商机来源", prop: "sales_sources_id", min: 190 }, // 商机来源
          { label: "省份", prop: "province", min: 190 }, // 省份
          { label: "城市", prop: "city", min: 190 }, // 城市
          { label: "负责人", prop: "owner", min: 190 } // 负责人
@@ -97,7 +96,20 @@
      }
    },
    // 请求数据
    async getData() {},
    async getData() {
      await getSalesLeadsList()
        .then((res) => {
          console.log(res)
          this.tableList.tableInfomation = res.data.tableList.map((item) => {
            item.province = item.Province.name
            item.city = item.City.name
            return item
          })
        })
        .catch((err) => {
          console.log(err)
        })
    },
    // 新建
    addBtnClick() {
      this.editSalesLeadConfig.visible = true
@@ -142,6 +154,11 @@
        region: "1",
        address: ""
      }
    },
    // 导入
    importClitk() {
      this.importConfig.visible = true
      this.importConfig.title = "销售线索"
    }
  }
}
src/views/other/commonDialog/EditDropdownDialog.vue
New file
@@ -0,0 +1,181 @@
<template>
  <div class="edit-dropdown-box">
    <el-dialog
      :title="'编辑下拉框>' + editDropdownConfig.title"
      :visible.sync="editConfig.editVisible"
      :width="dialogWidth"
      :before-close="handleClose"
      :append-to-body="true"
    >
      <el-table :data="tableData" style="width: 100%" :header-cell-style="{ background: '#f4f8fe' }">
        <el-table-column label="名称" prop="name">
          <template slot-scope="scope">
            <el-input v-model="scope.row.name" size="mini"></el-input>
          </template>
        </el-table-column>
        <el-table-column label="显示颜色" prop="color">
          <template slot-scope="scope">
            <el-color-picker v-model="scope.row.color" size="small" @change="colorClick(scope.row)"></el-color-picker>
          </template>
        </el-table-column>
        <el-table-column label="设为默认" prop="setDefault">
          <template slot-scope="scope">
            <el-switch
              v-model="scope.row.setDefault"
              active-color="#2E68DB"
              inactive-color="#AEB9CA"
              size="mini"
              @change="setDefaultClick(scope.row)"
            >
            </el-switch>
          </template>
        </el-table-column>
        <el-table-column label="操作" width="110px">
          <template slot-scope="scope">
            <i
              v-if="scope.$index !== tableData.length - 1"
              class="el-icon-bottom common-style"
              title="下移"
              @click="moveDownClick(scope.$index, scope.row)"
            ></i>
            <i
              v-if="scope.$index !== 0"
              class="el-icon-top common-style"
              title="上移"
              @click="moveUpClick(scope.$index, scope.row)"
            ></i>
            <i class="el-icon-delete common-style" title="删除" @click="deleteClick(scope.$index, scope.row)"></i>
          </template>
        </el-table-column>
      </el-table>
      <div style="padding: 10px">
        <el-button type="text" size="mini" @click="addDropdown">新增下拉框</el-button>
        <el-button type="text" size="mini">恢复默认颜色</el-button>
      </div>
      <div slot="footer">
        <el-button type="primary" size="small" @click="editConfig.editVisible = false">保存</el-button>
        <el-button size="small" @click="editConfig.editVisible = false">取消</el-button>
      </div>
    </el-dialog>
  </div>
</template>
<script>
export default {
  name: "EditDropdownDialog",
  props: {
    editDropdownConfig: {
      type: Object,
      default: () => {
        return {
          editVisible: false,
          title: "",
          infomation: {
            name: "",
            color: "",
            setDefault: ""
          }
        }
      }
    }
  },
  components: {},
  computed: {},
  data() {
    return {
      dialogWidth: "20%",
      editConfig: this.editDropdownConfig,
      tableData: [
        {
          name: "1",
          color: "red",
          setDefault: true
        },
        {
          name: "2",
          color: "blue",
          setDefault: false
        },
        {
          name: "3",
          color: null,
          setDefault: false
        },
        {
          name: "4",
          color: null,
          setDefault: false
        }
      ]
    }
  },
  created() {},
  methods: {
    handleClose() {
      this.editConfig.editVisible = false
    },
    // 颜色选择
    colorClick(row) {
      console.log(row)
    },
    // 设为默认
    setDefaultClick(row) {
      if (row.setDefault) {
        if (this.tableData !== null) {
          this.tableData = this.tableData.map((item) => {
            if (item.name !== row.name) {
              item.setDefault = false
            }
            return item
          })
        }
      }
    },
    // 上移
    moveUpClick(index) {
      var that = this
      const upData = that.tableData[index - 1]
      that.tableData.splice(index - 1, 1)
      that.tableData.splice(index, 0, upData)
    },
    // 下移
    moveDownClick(index) {
      var that = this
      const downData = that.tableData[index + 1]
      that.tableData.splice(index + 1, 1)
      that.tableData.splice(index, 0, downData)
    },
    // 删除
    deleteClick(row) {
      let id = this.tableData.findIndex((item) => {
        if (item.name === row.name) {
          return true
        }
      })
      this.tableData.splice(id, 1)
    },
    // 新增下拉框
    addDropdown() {
      this.tableData.push({
        name: "5",
        color: null,
        setDefault: false
      })
    }
  }
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style lang="scss" scoped>
// .edit-dropdown-box {
// }
.common-style {
  font-size: 16px;
  margin-left: 10px;
  cursor: pointer;
}
.first {
  margin-left: 0px;
}
</style>
src/views/other/commonDialog/HighViewScopeDialog.vue
@@ -21,6 +21,7 @@
          ref="tree"
          highlight-current
          :props="defaultProps"
          :filter-node-method="filterNode"
        >
        </el-tree>
      </div>
@@ -48,6 +49,11 @@
  },
  components: {},
  computed: {},
  watch: {
    searchInput(val) {
      this.$refs.tree.filter(val)
    }
  },
  data() {
    return {
      dialogWidth: "35%",
@@ -129,6 +135,10 @@
  methods: {
    handleClose() {
      this.editConfig.visible = false
    },
    filterNode(value, data) {
      if (!value) return true
      return data.label.indexOf(value) !== -1
    }
  }
}
src/views/other/commonDialog/ImportFileDialog.vue
New file
@@ -0,0 +1,125 @@
<template>
  <el-dialog
    :title="'导入' + importFileConfig.title"
    :visible.sync="editConfig.visible"
    :width="dialogWidth"
    :before-close="handleClose"
    :append-to-body="true"
  >
    <div class="import-body-bg">
      <el-steps :active="1">
        <el-step title="上传文件"></el-step>
        <el-step title="映射字段"></el-step>
        <el-step title="导入数据"></el-step>
        <el-step title="完成"></el-step>
      </el-steps>
      <div class="step">
        <div>1.请按照数据模板的格式准备要导入的数据</div>
        <el-button type="text" size="mini">下载数据模板</el-button>
        <div class="note">{{ "查看注意事项>>" }}</div>
      </div>
      <div class="step">
        <div>{{ "2.请设置" + editConfig.title + "查重字段" }}</div>
        <el-select v-model="setValue" size="mini">
          <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </el-option>
        </el-select>
        <el-select v-model="maybe" class="maybe-view" size="mini">
          <el-option v-for="item in maybeOptions" :key="item.value" :label="item.label" :value="item.value">
          </el-option>
        </el-select>
        <i class="el-icon-delete" title="删除" @click="deleteClick"></i>
        <div>
          <el-button type="info" size="mini" plain>新条件</el-button>
        </div>
      </div>
      <div class="step">
        <div>{{ "3.请设置" + editConfig.title + "重复时导入方式" }}</div>
        <el-select v-model="importMethod" size="mini">
          <el-option v-for="item in importMethodOptions" :key="item.value" :label="item.label" :value="item.value">
          </el-option>
        </el-select>
      </div>
      <div class="step">
        <div>4.请选择需要导入的文件(请使用系统导出的Excel模板)</div>
        <el-button type="info" size="mini" plain>添加文件</el-button>
        <div class="note">
          {{ "支持文件类型:.xls/.xlsx,不支持WPS格式(但是,可以在WPS中编辑后另存为*.xls格式再导入)" }}
        </div>
      </div>
    </div>
    <div slot="footer">
      <el-button type="primary" size="small" @click="editConfig.visible = false">下一步</el-button>
    </div>
  </el-dialog>
</template>
<script>
export default {
  name: "EditImportFileDialog",
  props: {
    importFileConfig: {
      type: Object,
      default: () => {
        return {
          visible: false,
          title: "",
          infomation: {
            name: "",
            color: "",
            setDefault: ""
          }
        }
      }
    }
  },
  components: {},
  computed: {},
  data() {
    return {
      dialogWidth: "45%",
      editConfig: this.importFileConfig,
      setValue: "联系人姓名",
      options: [],
      maybe: "或",
      maybeOptions: [],
      importMethod: "跳过",
      importMethodOptions: []
    }
  },
  created() {},
  methods: {
    handleClose() {
      this.editConfig.visible = false
    },
    deleteClick() {}
  }
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style lang="scss" scoped>
.import-body-bg {
  margin: 20px 40px;
  .step {
    margin-top: 10px;
    .note,
    .el-button,
    .el-select {
      margin-left: 20px;
      font-size: 12px;
      margin-top: 10px;
    }
    .maybe-view {
      width: 60px;
      margin-right: 20px;
    }
  }
}
::v-deep {
  .el-step__icon,
  .el-step__title {
    font-size: 13px;
  }
}
</style>
src/views/other/login/index.vue
@@ -23,13 +23,13 @@
                  </el-input>
                  <i id="iconfont" class="iconfont icon-biyanjing" @click="clickeye()"></i>
                </el-form-item>
                <!-- <el-form-item prop="code">
                <el-form-item prop="code">
                  <el-input type="text" v-model="formLogin.code" placeholder="验证码">
                    <template slot="append">
                      <img class="login-code" :src="captcha" @click="getCaptchaData" />
                    </template>
                  </el-input>
                </el-form-item> -->
                </el-form-item>
                <el-button size="default" @click="submit" type="primary" class="button-login"> 登录 </el-button>
              </el-form>
            </el-card>
@@ -51,6 +51,7 @@
<script>
// import { mapActions } from "vuex"
// import "@/assets/font/iconfont"
import { getCaptcha, login } from "@/api/login/login.js"
export default {
  name: "LoginView",
@@ -104,7 +105,15 @@
    clearInterval(this.timeInterval)
  },
  methods: {
    getCaptchaData() {},
    getCaptchaData() {
      getCaptcha().then((res) => {
        console.log(res)
        if (res.code == 200) {
          this.captchaId = res.data.captchaId
          this.captcha = res.data.picPath
        }
      })
    },
    /**
     * @description 提交表单
@@ -114,27 +123,21 @@
      this.$refs.loginForm.validate((valid) => {
        if (valid) {
          // 登录
          // 注意 这里的演示没有传验证码
          // 具体需要传递的数据请自行修改代码
          // this.login({
          //   username: this.formLogin.username,
          //   password: this.formLogin.password,
          //   captcha: this.formLogin.code
          // }).then((rsp) => {
          //   // 重定向对象不存在则返回顶层路径
          //   if (rsp != "") {
          //     // this.$message.warning(rsp);
          //     this.error = "填入信息错误,请检查!"
          //     let _this = this
          //     setTimeout(function () {
          //       _this.error = ""
          //     }, 3000)
          //     return
          //   }
          this.$router.replace(this.$route.query.redirect || "/").catch((err) => {
            console.log(err)
          login({
            username: this.formLogin.username,
            password: this.formLogin.password,
            captcha: this.formLogin.code,
            captchaId: this.captchaId
          })
          // })
            .then((res) => {
              console.log("成功" + res)
              this.$router.replace(this.$route.query.redirect || "/").catch((err) => {
                console.log(err)
              })
            })
            .catch((err) => {
              console.log(err)
            })
        } else {
          // 登录表单校验失败
          // this.$message.error("表单校验失败,请检查");
vue.config.js
@@ -3,17 +3,49 @@
//   transpileDependencies: true
// })
const path = require("path")
const getServerJson = require("./public/serverconfig")
const devServer = require("./vue.develop.config")
const { context } = getServerJson
// 基础路径 注意发布之前要先修改这里
const publicPath = "/"
function resolve(dir) {
  return path.join(__dirname, dir)
}
module.exports = {
  publicPath: context,
  publicPath,
  lintOnSave: false,
  productionSourceMap: false, // 如果你不需要生产环境的source map, 可以将其设置为false 以加速生产环境构建
  devServer,
  devServer: {
    proxy: {
      "/api/base": {
        target: "http://192.168.20.118:8001",
        ws: true,
        changeOrigin: true
      },
      "/api/salesLeads": {
        target: "http://192.168.20.118:8001",
        ws: true,
        changeOrigin: true
      },
      "/api/followRecord": {
        target: "http://192.168.20.118:8001",
        ws: true,
        changeOrigin: true
      },
      "/api-s": {
        target: "http://192.168.20.119:9081",
        ws: true,
        changeOrigin: true
      },
      "/api": {
        target: "http://192.168.20.118:8001",
        ws: true,
        changeOrigin: true
      }
      // "/api-s": {
      //   target: "http://192.168.20.113:9081",
      //   ws: true,
      //   changeOrigin: true,
      // },
    }
  },
  transpileDependencies: [
    // 兼容IE11浏览器(兼容npm包和cnpm包)
    "crypto-js",
vue.develop.config.js
File was deleted
yarn.lock
New file
Diff too large