Quellcode durchsuchen

Merge branch 'master' of guanli/ngds-backend-ele into develop

Tyler vor 10 Monaten
Ursprung
Commit
366803ef3d
40 geänderte Dateien mit 2863 neuen und 327 gelöschten Zeilen
  1. 13 11
      .env.development
  2. 15 12
      .env.staging
  3. 3 2
      package.json
  4. 1 1
      src/api/bonus.js
  5. 57 0
      src/api/config.js
  6. 2 1
      src/api/member.js
  7. 1 0
      src/api/shop.js
  8. 25 0
      src/api/site.js
  9. 1 1
      src/components/Charts/Keyboard.vue
  10. 1 1
      src/components/Charts/LineMarker.vue
  11. 1 1
      src/components/Charts/MixChart.vue
  12. 487 0
      src/components/TreeChart/index.vue
  13. 60 8
      src/components/Upload/index.vue
  14. 38 10
      src/lang/en.js
  15. 40 14
      src/lang/zh.js
  16. 22 6
      src/router/index.js
  17. 2 2
      src/router/modules/config.js
  18. 20 0
      src/router/modules/currency.js
  19. 6 0
      src/router/modules/member.js
  20. 21 0
      src/router/modules/transportation.js
  21. 23 0
      src/styles/element-ui.scss
  22. 6 0
      src/styles/index.scss
  23. 1 1
      src/styles/style.scss
  24. 409 0
      src/views/atlas/network-ex.vue
  25. 412 0
      src/views/atlas/relation-ex.vue
  26. 13 15
      src/views/atlas/relation-opt.vue
  27. 33 140
      src/views/bonus/period.vue
  28. 7 7
      src/views/config/bonus-config.vue
  29. 97 20
      src/views/config/exchange-rate.vue
  30. 6 6
      src/views/config/transfer-config.vue
  31. 138 0
      src/views/config/transportation-config.vue
  32. 2 2
      src/views/dashboard/admin/components/BarChart.vue
  33. 2 2
      src/views/dashboard/admin/components/LineChart.vue
  34. 2 2
      src/views/dashboard/admin/components/PieChart.vue
  35. 2 2
      src/views/dashboard/admin/components/RaddarChart.vue
  36. 428 32
      src/views/shop/goods-add.vue
  37. 369 20
      src/views/shop/index.vue
  38. 1 1
      src/views/user/change-highest-emp-level.vue
  39. 38 4
      src/views/user/empty-order-operation.vue
  40. 58 3
      src/views/user/member-list.vue

+ 13 - 11
.env.development

@@ -19,16 +19,18 @@ ENV='development'
 # VUE_APP_SYSTEM_JS='http://ng-upload.elken.com'
 
 # api请求地址
-VUE_APP_BASE_API=''
-# 文件下载地址
-VUE_APP_BASE_DO_API='http://16.163.228.151:8042'
-# CDN文件地址
-VUE_APP_CDN_API='http://172.17.133.86:9970'
-# 页面地址
-VUE_APP_BASE_WEBSITE='http://local.ng.backend.ele.com'
-# 会员端地址
-VUE_APP_FRONTEND_WEBSITE='http://local.ng.frontend.ele.com'
-# PayStack
+ VUE_APP_BASE_API='http://16.163.228.151:8020'
+ # 文件下载地址
+ VUE_APP_BASE_DO_API='http://16.163.228.151:8020'
+ # CDN文件地址
+ VUE_APP_CDN_API='http://16.163.228.151:8021'
+ # 区域js文件地址
+ VUE_APP_SYSTEM_JS='http://16.163.228.151:8021'
+ # 结算页面地址
+ VUE_APP_BASE_WEBSITE='http://16.163.228.151:8019'
+ # 会员页面地址
+ VUE_APP_FRONTEND_WEBSITE='http://16.163.228.151:8017'
+# PayStack支付key
 VUE_APP_BASE_PAY_STACK_PUBLIC_KEY='pk_test_2eed10135c4a958c5073795b22854ded9d1a6c55'
 # 请求token前缀
-VUE_APP_ACCESS_TOKEN_PREFIX='Bearer '
+VUE_APP_ACCESS_TOKEN_PREFIX='Bearer '

+ 15 - 12
.env.staging

@@ -3,19 +3,22 @@ NODE_ENV=production
 # just a flag
 ENV='staging'
 
-# api请求地址
-VUE_APP_BASE_API='http://16.163.228.151:8042'
-# 文件下载地址
-VUE_APP_BASE_DO_API='http://16.163.228.151:8042'
-# CDN文件地址
-VUE_APP_CDN_API='http://16.163.228.151:8042'
-# 区域js文件地址
-VUE_APP_SYSTEM_JS='http://ng-upload.elken.com'
-# 结算页面地址
-VUE_APP_BASE_WEBSITE='http://16.163.228.151:8042'
-# 会员页面地址
-VUE_APP_FRONTEND_WEBSITE='http://16.163.228.151:8045'
 # PayStack支付key
 VUE_APP_BASE_PAY_STACK_PUBLIC_KEY='pk_test_2eed10135c4a958c5073795b22854ded9d1a6c55'
 # 请求token前缀
 VUE_APP_ACCESS_TOKEN_PREFIX='Bearer '
+
+# test
+# # api请求地址
+ VUE_APP_BASE_API='http://16.163.228.151:8020'
+ # 文件下载地址
+ VUE_APP_BASE_DO_API='http://16.163.228.151:8020'
+ # CDN文件地址
+ VUE_APP_CDN_API='http://16.163.228.151:8021'
+ # 区域js文件地址
+ VUE_APP_SYSTEM_JS='http://16.163.228.151:8021'
+ # 结算页面地址
+ VUE_APP_BASE_WEBSITE='http://16.163.228.151:8019'
+ # 会员页面地址
+ VUE_APP_FRONTEND_WEBSITE='http://16.163.228.151:8017'
+

+ 3 - 2
package.json

@@ -8,6 +8,7 @@
     "lint": "eslint --ext .js,.vue src",
     "build:prod": "vue-cli-service build",
     "build:stage": "vue-cli-service build --mode staging",
+    "build:dev": "vue-cli-service build --mode development",
     "preview": "node build/index.js --preview",
     "new": "plop",
     "svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml",
@@ -19,10 +20,9 @@
     "axios": "0.18.1",
     "clipboard": "2.0.4",
     "codemirror": "5.45.0",
-    "core-js": "3.6.5",
+    "core-js": "^3.32.0",
     "driver.js": "0.9.5",
     "dropzone": "5.5.1",
-    "echarts": "4.2.1",
     "element-ui": "2.13.2",
     "file-saver": "2.0.1",
     "fuse.js": "3.4.4",
@@ -66,6 +66,7 @@
     "chalk": "2.4.2",
     "chokidar": "2.1.5",
     "connect": "3.6.6",
+    "echarts": "^5.4.3",
     "html-webpack-plugin": "3.2.0",
     "husky": "1.3.1",
     "lint-staged": "8.1.5",

+ 1 - 1
src/api/bonus.js

@@ -230,7 +230,7 @@ export function fetchClosePeriod(query) {
 
 export function fetchCalcPeriod(query) {
   return request({
-    url: '/v1/calc/calc-period/' + query,
+    url: '/v1/bonus/calc-period/' + query,
     method: 'get'
   })
 }

+ 57 - 0
src/api/config.js

@@ -258,3 +258,60 @@ export function updateOtherConfig(data) {
     data
   })
 }
+
+/**
+ * 获取货币
+ * @returns {*}
+ */
+export function fetchCurrenciesList() {
+  return request({
+    url: '/v1/currency/currencies',
+    method: 'get'
+  })
+}
+
+/**
+ * 获取汇率列表
+ * @returns {*}
+ */
+export function fetchCurrencyConversionsList() {
+  return request({
+    url: '/v1/currency/currencies-conversions',
+    method: 'get'
+  })
+}
+
+/**
+ * 设置汇率
+ * @returns {*}
+ */
+export function setCurrencyConversions(data) {
+  return request({
+    url: '/v1/currency/set-currencies-conversions',
+    method: 'post',
+    data
+  })
+}
+
+/**
+ * 获取运费列表
+ * @returns {*}
+ */
+export function fetchTransportationList() {
+  return request({
+    url: '/v1/transportation/transportation',
+    method: 'get'
+  })
+}
+
+/**
+ * 设置汇率
+ * @returns {*}
+ */
+export function setTransportation(data) {
+  return request({
+    url: '/v1/transportation/set-transportation',
+    method: 'post',
+    data
+  })
+}

+ 2 - 1
src/api/member.js

@@ -340,10 +340,11 @@ export function updateNetworkMoveApply(data) {
  * 空单操作查询
  * @returns {*}
  */
-export function fetchUserAdd() {
+export function fetchUserAdd(data) {
   return request({
     url: '/v1/user/user-add',
     method: 'get',
+    params: data
   })
 }
 

+ 1 - 0
src/api/shop.js

@@ -208,3 +208,4 @@ export function orderDetail(query) {
 
 
 
+

+ 25 - 0
src/api/site.js

@@ -34,3 +34,28 @@ export function getBaseInfo() {
     method: 'get'
   })
 }
+
+/**
+ * 国家
+ * @returns {*}
+ */
+export function getCountries() {
+  return request({
+    url: '/v1/site/countries',
+    method: 'get'
+  })
+}
+
+export function getLanguages() {
+  return request({
+    url: '/v1/site/languages',
+    method: 'get'
+  })
+}
+export function getBank(query) {
+  return request({
+    url: '/v1/site/banks',
+    method: 'get',
+    params: query
+  })
+}

+ 1 - 1
src/components/Charts/Keyboard.vue

@@ -3,7 +3,7 @@
 </template>
 
 <script>
-import echarts from 'echarts'
+import * as echarts from 'echarts'
 import resize from './mixins/resize'
 
 export default {

+ 1 - 1
src/components/Charts/LineMarker.vue

@@ -3,7 +3,7 @@
 </template>
 
 <script>
-import echarts from 'echarts'
+import * as echarts from 'echarts'
 import resize from './mixins/resize'
 
 export default {

+ 1 - 1
src/components/Charts/MixChart.vue

@@ -3,7 +3,7 @@
 </template>
 
 <script>
-import echarts from 'echarts'
+import * as echarts from 'echarts'
 import resize from './mixins/resize'
 
 export default {

+ 487 - 0
src/components/TreeChart/index.vue

@@ -0,0 +1,487 @@
+<template>
+  <div id="main" class="echarts-container" />
+</template>
+<script>
+import { TreeChart } from "echarts/charts";
+import { TooltipComponent } from "echarts/components";
+import * as echarts from "echarts/core";
+import { CanvasRenderer } from "echarts/renderers";
+
+echarts.use([TooltipComponent, TreeChart, CanvasRenderer]);
+// let this.myChart;
+let option;
+
+var treeNodePadding = 50; //节点间最小间隔
+
+var treeTopPadding = 120; //tree距顶端的距离
+
+var rightNode; //最右侧节点,用于计算偏移量
+export default {
+  name: "TreeChart",
+  data() {
+    return {
+      myChart: null,
+      centerLoca: [],
+      fontSize: 6,
+      chartWidth: 0,
+      chartHeight: 0,
+      center: [this.chartWidth, "50%"],
+    };
+  },
+  props: {
+    treeData: {
+      type: Object,
+      default: () => {
+        return {};
+      },
+    },
+    topDeep: Number,
+    type: String,
+    clickNodeList: Array,
+  },
+  watch: {
+    clickNodeList: {
+      handler(newValue, old) {
+        if (newValue) {
+          this.init();
+        }
+      },
+    },
+    treeData: {
+      handler(newValue, old) {
+        console.log(newValue);
+        if (newValue) {
+          this.init();
+        }
+      },
+    },
+    chartWidth: {
+      handler(newValue, old) {
+        console.log(newValue);
+        if (newValue) {
+          this.init();
+        }
+      },
+    },
+  },
+  mounted() {
+    this.init();
+    // 监听树图节点的点击事件
+    this.myChart.on("click", (e) => {
+      console.log("e:", e);
+      this.$emit("clickNode", e);
+    });
+  },
+  methods: {
+    init() {
+      const that = this;
+      // console.log('$el:', this.$el)
+      that.myChart = echarts.init(this.$el);
+      that.myChart.clear();
+      option = {
+        tooltip: {
+          // 提示框浮层设置
+          trigger: "item",
+          triggerOn: "mousemove", // 提示框触发条件
+          enterable: true, // 鼠标是否可进入提示框浮层中,默认false
+          confine: true, // 是否将tooltip框限制在图表的区域内
+          padding: [5, 10],
+          formatter: function (params) {
+            // 提示框浮层内容格式器,支持字符串模板和回调函数两种形式
+            const relationStr =
+              "<div style='display: flex;flex-direction: column;'>" +
+              "<div>" +
+              '<span style="color: #606266;">' +
+              that.$t("atlas.numberLayers") +
+              ": " +
+              "</span>" +
+              '<span style="color: #000000;">' +
+              (Number(params.data.TOP_NETWORK_DEEP) - Number(that.topDeep)) +
+              "</span>" +
+              "</div>" +
+              "<div>" +
+              '<span style="color: #606266;">' +
+              that.$t("atlas.memberCode") +
+              ": " +
+              "</span>" +
+              '<span style="color: #000000;">' +
+              params.data.USER_NAME +
+              "</span>" +
+              "</div>" +
+              "<div>" +
+              '<span style="color: #606266;">' +
+              that.$t("atlas.name") +
+              ": " +
+              "</span>" +
+              '<span style="color: #000000;">' +
+              params.data.REAL_NAME +
+              "</span>" +
+              "</div>" +
+              "<div>" +
+              '<span style="color: #606266;">' +
+              that.$t("atlas.level") +
+              ": " +
+              "</span>" +
+              '<span style="color: #000000;">' +
+              params.data.DEC_LV_NAME +
+              "</span>" +
+              "</div>" +
+              "<div>" +
+              '<span style="color: #606266;">' +
+              that.$t("atlas.highest") +
+              ": " +
+              "</span>" +
+              '<span style="color: #000000;">' +
+              params.data.EMP_LV_NAME +
+              "," +
+              params.data.CROWN_LV_NAME +
+              "</span>" +
+              "</div>" +
+              "<div>" +
+              '<span style="color: #606266;">' +
+              that.$t("bonus.periodNum") +
+              ": " +
+              "</span>" +
+              '<span style="color: #000000;">' +
+              params.data.PERIOD_AT +
+              "</span>" +
+              "</div>" +
+              "</div>";
+            const networkStr =
+              "<div style='display: flex;flex-direction: column;'>" +
+              "<div>" +
+              '<span style="color: #606266;">' +
+              that.$t("atlas.memberCode") +
+              ": " +
+              "</span>" +
+              '<span style="color: #000000;">' +
+              params.data.USER_NAME +
+              "</span>" +
+              "</div>" +
+              "<div>" +
+              '<span style="color: #606266;">' +
+              that.$t("atlas.name") +
+              ": " +
+              "</span>" +
+              '<span style="color: #000000;">' +
+              params.data.REAL_NAME +
+              "</span>" +
+              "</div>" +
+              "<div>" +
+              '<span style="color: #606266;">' +
+              that.$t("atlas.level") +
+              ": " +
+              "</span>" +
+              '<span style="color: #000000;">' +
+              params.data.DEC_LV_NAME +
+              "</span>" +
+              "</div>" +
+              "<div>" +
+              '<span style="color: #606266;">' +
+              that.$t("atlas.highest") +
+              ": " +
+              "</span>" +
+              '<span style="color: #000000;">' +
+              params.data.EMP_LV_NAME +
+              "</span>" +
+              "</div>" +
+              "<div>" +
+              '<span style="color: #606266;">' +
+              that.$t("bonus.periodNum") +
+              ": " +
+              "</span>" +
+              '<span style="color: #000000;">' +
+              params.data.PERIOD_AT +
+              "</span>" +
+              "</div>" +
+              "</div>";
+
+            return that.type === "placement" ? relationStr : networkStr;
+          },
+          // valueFormatter: function (value) { // tooltip 中数值显示部分的格式化回调函数
+          //   return '$' + value.toFixed(2)
+          // },
+          backgroundColor: "rgba(250,250,250,0.99)", // 提示框浮层的背景颜色
+          borderColor: "#1890FF", // 提示框浮层的边框颜色
+          borderWidth: 0.5, // 提示框浮层的边框宽
+          borderRadius: 8, // 提示框浮层圆角
+          textStyle: {
+            // 提示框浮层的文本样式
+            color: "#333", // 文字颜色
+            // fontWeight: 400, // 字体粗细
+            // fontSize: that.fontSize, // 字体大小
+            // lineHeight: 20, // 行高
+            // width: 60, // 文本显示宽度
+            // 文字超出宽度是否截断或者换行;只有配置width时有效
+            overflow: "breakAll", // truncate截断,并在末尾显示ellipsis配置的文本,默认为...;break换行;breakAll换行,并强制单词内换行
+            ellipsis: "...",
+          },
+          extraCssText:
+            "min-height:100px;box-shadow: 0 0 9px rgba(0, 0, 0, 0.3);text-align: left;", // 额外添加到浮层的css样式
+          axisPointer: {
+            type: "shadow",
+            shadowStyle: {
+              color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+                { offset: 0, color: "rgba(255, 255, 255, 0)" },
+                { offset: 1, color: "rgba(37, 107, 230, 0.18)" },
+              ]),
+            },
+          },
+        },
+        series: [
+          {
+            width: that.chartWidth + "px",
+            height: "100%",
+            type: "tree",
+            data: [this.treeData],
+            name: "树图",
+            top: "1%", // 组件离容器上侧的距离,像素值20,或相对容器的百分比
+            left: "1%", // 组件离容器左侧的距离
+            bottom: "1%", // 组件离容器下侧的距离
+            right: "1%", // 组件离容器右侧的距离
+            layout: "orthogonal", // 树图的布局,正交orthogonal和径向radial两种
+            orient: "TB", // 树图中正交布局的方向,'LR','RL','TB','BT',只有布局是正交时才生效
+            edgeShape: "polyline", // 树图边的形状,有曲线curve和折线polyline两种,只有正交布局下生效
+            // edgeForkPosition:"10%",
+            roam: true, // 是否开启鼠标缩放或平移,默认false
+            zoom: 0.5,
+            scaleLimit: {
+              min: 0.5,
+              max: 10,
+            },
+            center: that.center || null,
+            // center: [5, 5],
+            initialTreeDepth: -1, // 树图初始的展开层级(深度),根节点是0,不设置时全部展开
+            // symbol: 'emptyCircle', // 标记的图形,默认是emptyCircle;circle,rect,roundRect,triangle,diamond,pin,arrow,none
+            symbol: function (value, params) {
+              // params.data节点的所有数据
+              if (params.data.leaf == true) {
+                return "emptyCircle";
+              } else if (params.data.leaf == false) {
+                return "circle";
+              }
+            },
+            // symbolRotate: 270, // 配合arrow图形使用效果较好
+            symbolSize: 10, // 大于0时是圆圈,等于0时不展示,标记的大小
+            itemStyle: {
+              // 树图中每个节点的样式
+              color: "#1890FF", // 节点未展开时的填充色
+              borderColor: "rgba(255, 144, 0, 1)", // 图形的描边颜色
+              borderWidth: 0.5, // 描边线宽,为0时无描边
+              borderType: "dotted", // 描边类型
+              borderCap: "square", // 指定线段末端的绘制方式butt方形结束,round圆形结束,square
+              shadowColor: "rgba(0,121,221,0.3)", // 阴影颜色
+              shadowBlur: 12, // 图形阴影的模糊大小
+              opacity: 1, // 图形透明度
+            },
+            label: {
+              // 每个节点对应的文本标签样式
+              show: true, // 是否显示标签
+              // distance: 22, // 文本距离图形元素的距离
+              position: "bottom", // 标签位置
+              verticalAlign: "middle", // 文字垂直对齐方式,默认自动,top,middle,bottom
+              align: "center", // 文字水平对齐方式,默认自动,left,right,center
+              fontSize: that.fontSize, // 字体大小
+              color: "#333", // 字体颜色
+              backgroundColor: "#F0F5FA", // 文字块的背景颜色
+              borderColor: "#1890FF", // 文字块边框颜色
+              borderWidth: 0.5, // 文字块边框宽度
+              borderType: "solid", // 文字块边框描边类型 solid dashed dotted
+              borderRadius: 6, // 文字块的圆角
+              padding: [5, 6], // 文字块内边距
+              shadowColor: "rgba(0,121,221,0.6)", // 文字块的背景阴影颜色
+              shadowBlur: 6, // 文字块的背景阴影长度
+              width: 80,
+              // 文字超出宽度是否截断或者换行;只有配置width时有效
+              overflow: "truncate", // truncate截断,并在末尾显示ellipsis配置的文本,默认为...;break换行;breakAll换行,并强制单词内换行
+              ellipsis: "...",
+              formatter: function (params) {
+                return (
+                  params.data.USER_NAME + "\n" + "\n" + params.data.REAL_NAME
+                );
+                // return params.data.REAL_NAME
+              },
+            },
+
+            lineStyle: {
+              // 树图边的样式
+              color: "rgba(0,0,0,.35)", // 树图边的颜色
+              width: 1, // 树图边的宽度
+              curveness: 0.5, // 树图边的曲度
+              shadowColor: "rgba(0, 0, 0, 0.5)", // 阴影颜色
+              shadowBlur: 10, // 图形阴影的模糊大小
+            },
+            emphasis: {
+              // 树图中图形和标签高亮的样式
+              disabled: false, // 是否关闭高亮状态,默认false
+              // 在高亮图形时,是否淡出其它数据的图形已达到聚焦的效果
+              focus: "relative", // none不淡出其他图形(默认);self只聚焦当前高亮的数据图形;series聚焦当前高亮的数据所在系列的所有图形;ancestor聚焦所有祖先节点;descendant聚焦所有子孙节点;relative聚焦所有子孙和祖先节点
+              blurScope: "coordinateSystem", // 开启focus时,配置淡出的范围,coordinateSystem淡出范围为坐标系(默认);series淡出范围为系列;global淡出范围为全局
+              itemStyle: {
+                // 该节点的样式
+                color: "#1890FF", // 图形的颜色
+                // borderColor: 'rgba(255, 144, 0, 1)', // 图形的描边颜色
+                // borderWidth: 1, // 描边线宽,为0时无描边
+                borderType: "solid", // 描边类型 solid dashed dotted
+                borderCap: "square", // 指定线段末端的绘制方式butt方形结束,round圆形结束,square
+                shadowColor: "rgba(0,121,221,0.3)", // 阴影颜色
+                shadowBlur: 12, // 图形阴影的模糊大小
+                opacity: 1, // 图形透明度
+              },
+              lineStyle: {
+                // 树图边的样式
+                color: "rgba(0,0,0,.45)", // 树图边的颜色
+                width: 2, // 树图边的宽度
+                curveness: 0.5, // 树图边的曲度
+                shadowColor: "rgba(0, 0, 0, 0.5)", // 阴影颜色
+                shadowBlur: 6, // 图形阴影的模糊大小
+              },
+              label: {
+                // 高亮标签的文本样式
+                color: "#333",
+                fontWeight: 600,
+              },
+            },
+            blur: {
+              // 淡出状态的相关配置,开启emphasis.focus后有效
+              itemStyle: {}, // 节点的样式
+              lineStyle: {}, // 树图边的样式
+              label: {}, // 淡出标签的文本样式
+            },
+            leaves: {
+              // 叶子节点的特殊配置
+              label: {
+                // 叶子节点的文本标签样式
+                // distance: 22,
+                // color: '#1890FF',
+                position: "bottom",
+                verticalAlign: "middle",
+                align: "center",
+              },
+              itemStyle: {}, // 叶子节点的样式
+              emphasis: {}, // 叶子节点高亮状态的配置
+              blur: {}, // 叶子节点淡出状态的配置
+              select: {}, // 叶子节点选中状态的配置
+            },
+            animation: true, // 是否开启动画
+            expandAndCollapse: true, // 子树折叠和展开的交互,默认打开
+            animationDuration: 500, // 初始动画的时长
+            animationEasing: "linear", // 初始动画的缓动效果
+            animationDelay: 0, // 初始动画的延迟
+            animationDurationUpdate: 500, // 数据更新动画的时长
+            animationEasingUpdate: "cubicInOut", // 数据更新动画的缓动效果
+            animationDelayUpdate: 0, // 数据更新动画的延迟
+          },
+        ],
+      };
+
+      option && that.myChart.setOption(option, true);
+      that.resize();
+
+      // that.adjustTreeView();
+      that.myChart.getZr().off("mousewheel");
+      that.myChart.getZr().on("mousewheel", (param) => {
+        let currentOption = that.myChart.getOption();
+        // console.log(currentOption);
+        if (currentOption.series[0]) {
+          let zoom = currentOption.series[0].zoom;
+          // that.fontSize = 10 * zoom
+          currentOption.textStyle.fontSize = 10 * zoom;
+          currentOption.series[0].label.fontSize = 8 * zoom;
+          currentOption.series[0].label.width = 110 * zoom;
+          currentOption.series[0].label.distance = 18 * zoom;
+          currentOption.series[0].leaves.distance = 18 * zoom;
+          option && that.myChart.setOption(currentOption);
+        }
+      });
+    },
+
+    resize() {
+      let elesArr = Array.from(
+        new Set(this.myChart._chartsViews[0]._data._graphicEls)
+      );
+      console.log(elesArr.length)
+      let dep = this.myChart._chartsViews[0]._data.tree.root.height; //获取树高
+      console.log(dep);
+      let layer_height = 90; //层级之间的高度
+      let currentHeight = layer_height * (dep + 1) || layer_height;
+      let newHeight = Math.max(currentHeight, layer_height);
+      this.chartHeight = newHeight + "px";
+      let layer_width = 90; // 兄弟节点之间的距离
+      let currentWidth = layer_width * (elesArr.length - 1) || layer_width;
+      let newWidth = Math.max(currentWidth, layer_width);
+      console.log(newWidth);
+      if (newWidth < 200) {
+        this.center[0] = '-60%';
+        this.center[1] = (dep * 2) + '0%';
+
+      }else if (newWidth > 200 && newWidth < 500) {
+        this.center[0] = '-56%';
+        this.center[1] = (dep * 1.5) + '0%';
+
+      }else if (newWidth > 500 && newWidth < 1000) {
+        this.center[0] = '-20%';
+        this.center[1] = (dep) + '0%';
+
+      } else if (newWidth > 1000){
+        this.center[0] = elesArr.length > 60 ? newWidth * 1.5 : newWidth * 1.2;
+        this.center[1] = (dep ) + '0%';
+      }
+      this.chartWidth = newWidth;
+
+      window.addEventListener("resize", () => {
+        this.myChart.resize();
+      });
+    },
+
+    doGlobalTreeChart(ec, data) {
+      // this.myChart.setOption(getGlobalTreeOption());
+      //生成图表后做调整
+      this.adjustTreeView();
+    },
+
+    //调整tree显示
+    adjustTreeView() {
+      var zr = this.myChart.getZr();
+      var domWidth = zr.painter.getWidth();
+      var treeWidth = this.getTreeWidth(zr);
+      if (treeWidth <= domWidth) return;
+    },
+
+    //计算最左边节点和最右边节点(symbol为image或icon)的间隔即为树图宽度
+
+    getTreeWidth(zr) {
+      var nodes = zr.storage._roots;
+      console.log(nodes);
+      let max = 0;
+      let min = 0;
+      for (var i = 0; i < nodes.length; i++) {
+        if (nodes[i].type == "image" || nodes[i].type == "icon") {
+          var nodeX = nodes[i].style.x;
+
+          if (nodeX > max) {
+            max = nodeX;
+
+            rightNode = nodes[i];
+
+            continue;
+          }
+
+          if (nodeX < min) {
+            min = nodeX;
+          }
+        }
+      }
+
+      return max - min;
+    },
+  },
+};
+</script>
+<style lang="scss" scoped>
+.echarts-container {
+  width: 100%;
+  height: calc(100vh - 105px);
+}
+</style>
+

+ 60 - 8
src/components/Upload/index.vue

@@ -12,15 +12,16 @@
       :before-upload="uploaderHandleBefore"
       :on-success="uploaderHandleSuccess"
       :disabled="uploaderDisabled"
-      :style="isImgUpload ? `width:${width};height:${height};` : ''"
+      :style="isImgUpload ? `width:${width};min-height:${height};` : ''"
     >
       <template v-if="isImgUpload">
-        <img
+        <el-image
           v-if="uploaderImageUrl"
           :src="uploaderImageUrl"
           class="image-preview"
-          :style="`width:${width};height:${height};display: block;`"
-        >
+          fit="contain"
+          :style="`width:${width};min-height:${height};display: block;`"
+        ></el-image>
         <i
           v-else
           class="el-icon-plus uploader-icon"
@@ -33,9 +34,18 @@
     </el-upload>
     <template v-if="isImgUpload">
       <div v-show="!uploaderShow" class="image-show">
-        <img :src="uploaderImageUrl" alt="" :width="width" :height="height">
+        <el-image :src="uploaderImageUrl" alt="" :width="width" :height="height"></el-image>
       </div>
     </template>
+
+    <div class="previewImg">
+      <el-button v-show="uploaderImageUrl" type="info" class="previewImgButton"  @click="previewImg">{{ $t('shop.imagePreview') }}</el-button>
+    </div>
+    <el-dialog append-to-body top="5vh" :visible.sync="dialogVisible" custom-class="custom-dialog">
+      <div class="previewImage">
+        <el-image width="60%" :src="uploaderImageUrl"  alt=""></el-image>
+      </div>
+    </el-dialog>
   </div>
 </template>
 
@@ -70,7 +80,7 @@ export default {
     },
     height: {
       type: String,
-      default: '100px'
+      default: '100%'
     }
   },
   data() {
@@ -88,7 +98,9 @@ export default {
       },
       uploaderDisabled: false,
       successImageUrl: null,
-      uploaderImage: null
+      uploaderImage: null,
+      dialogVisible: false,
+      uploadedImages: []
     }
   },
   computed: {
@@ -96,7 +108,6 @@ export default {
       if (this.uploaderImage != null) {
         return this.uploaderImage
       }
-      console.log(this.defaultImageUrl)
       if (this.successImageUrl !== null) {
         return this.successImageUrl
       } else if (this.defaultImageUrl !== null) {
@@ -120,6 +131,10 @@ export default {
     console.log(this.uploaderImage)
   },
   methods: {
+    previewImg(){
+      this.dialogVisible = true;
+    },
+
     uploaderHandleBefore() {
       const auth_token = localStorage.accessToken
       this.uploaderHeaders.Authorization = 'Bearer  ' + auth_token
@@ -164,6 +179,7 @@ export default {
   width: 100%;
   height: 100%;
   position: relative;
+  margin-bottom: 50px;
   .image-uploader {
     height: 100%;
   }
@@ -210,4 +226,40 @@ export default {
     }
   }
 }
+.previewImage{
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+.previewImg{
+  display: flex;
+  justify-content: center;
+  .previewImgButton{
+    position: absolute;
+    bottom: -50px;
+  }
+}
+.custom-dialog {
+  //background: rgba(0, 0, 0, 0); /* 半透明的背景 */
+  background-color: transparent!important;
+}
+
+.custom-dialog {
+  background-color: transparent; /* 使对话框的背景透明 */
+  box-shadow: none; /* 移除阴影 */
+}
+
+.custom-dialog .el-dialog__header,
+.custom-dialog .el-dialog__body,
+.custom-dialog .el-dialog__footer {
+  background-color: transparent; /* 透明的头部、内容和底部 */
+}
+
+.custom-dialog .el-dialog__header {
+  border-bottom: none; /* 移除对话框头部的边框 */
+}
+
+.custom-dialog .el-dialog__footer {
+  border-top: none; /* 移除底部的边框 */
+}
 </style>

+ 38 - 10
src/lang/en.js

@@ -81,6 +81,8 @@ export default {
     financeWithdraw:'Withdrawal management',
     atlasRelationOpt:'Sponsor network',
     atlasNetworkOpt:'Placement network',
+    sponsorNetworkEx:'Sponsor network Ex',
+    placementNetworkEx:'Placement network Ex',
     shopIndex:'Products list',
     shopGoodsAdd:'Add Product',
     shopOrderDecList:'External mall entry list',
@@ -214,7 +216,7 @@ export default {
     settlementDate: 'Settlement Date',
     networkConnectionTime: 'Network connection time',
     closurePeriod: 'Closure period',
-    logRefresh: 'Log refresh',
+    logRefresh: 'Bonus Log',
     actionTime: 'Operating time(WAT)',
     logContent: 'Log content',
     autoCalculation: 'Automatic calculation',
@@ -222,7 +224,7 @@ export default {
     initializeBasicData: 'Initialize basic data',
     generationPeriodPerformance: 'Generation period performance',
     pullPeriodPerformance: 'Pull period performance',
-    calculationOfBonus: 'Calculate bonus',
+    calculationOfBonus: 'Calculate',
     pullBonusData: 'Pull Bonus Data',
     bulkWithdrawal: 'Batch withdrawal',
     periodNum: 'Number of periods',
@@ -399,7 +401,9 @@ export default {
     hideSelectedData: 'Are you sure to hide the selected data?',
     unHideSelectedData: 'Are you sure to un-hide the selected data?',
     exportCurrentData: 'Are you sure you want to export the current data?',
-    all: 'All'
+    all: 'All',
+    countryName: 'Country Name',
+    language: "Language"
   },
 
   filter: {
@@ -475,7 +479,9 @@ export default {
     memberHighestEmpLevelAdjustment:'Highest Director Level Adjust',
     // Second level menu/interface-networkChart
     placementNetwork: 'Placement Network',
+    placementNetworkEx: 'Placement Network Ex',
     sponsorNetwork: 'Sponsor Network',
+    sponsorNetworkEx: 'Sponsor Network Ex',
     // Second level menu/interface-bonusManagement
     closurePeriod: 'Closure Period',
     memberBonusBalance: 'Member Bonus Balance',
@@ -516,7 +522,8 @@ export default {
     otherConfig: 'Other configuration',
     decLevelConfig: 'Member level configuration',
     empLevelConfig: 'Member rank level configuration',
-    decRoleConfig: 'Stockist level configuration'
+    decRoleConfig: 'Stockist level configuration',
+    transportationConfig: "Transportation Config",
   },
 
   // Member
@@ -527,7 +534,7 @@ export default {
     pleaseInputMemberCode: 'Please enter member code',
     pleaseSelectDirectorLevel: 'Please Select The Director Level',
     modifyHighestDirectorLevelHits: 'Do you want to modify the current member Director Level?',
-    modifyHighestDirectorLevelLevel: 'Modify member Director Level',
+    modifyHighestDirectorLevelLevel: 'Modified Director Level',
     selectDirectorLevelHint: 'Please Select The Director Level',
     modifyMemberDirectorLevelHits: 'Do you want to modify the current member Director Level?',
     currentLevel: 'Current Level',
@@ -634,13 +641,16 @@ export default {
     byPlacementNetwork: 'By placement network',
     byMembershipSystem: 'By membership system',
     byProvinceAndRegion: 'By province and region',
-    exportDataHint: 'Are you sure you want to export the current data?'
+    exportDataHint: 'Are you sure you want to export the current data?',
+    modifyCountry: "Whether to change the country?"
   },
 
   // Network
   network: {
     placementNetwork: 'Placement Network',
-    sponsorNetwork: 'Sponsor Network'
+    sponsorNetwork: 'Sponsor Network',
+    placementNetworkEx: 'Placement Network Ex',
+    sponsorNetworkEx: 'Sponsor Network Ex'
   },
 
   // Configuration
@@ -691,7 +701,10 @@ export default {
     memberAbout: 'Member related',
     homeAbout: 'Member management related',
     withdrawalAbout: 'Withdrawal related',
-    otherAbout: 'Others'
+    otherAbout: 'Others',
+    modifyShippingRates: 'Edit shipping rates',
+    modifyExchangeRate: 'Edit Exchange Rate',
+    refreshProductPricePrompt: 'After opening, the prices of listed products will be refreshed according to the new product exchange rate'
   },
 
   // Financial Management
@@ -813,9 +826,11 @@ export default {
     productCode: 'Product Code',
     unit: 'Unit',
     taxRate: 'Tax Rate',
+    enterTaxRate: 'Tax rate is required',
     uSPrice: 'US Price($)',
-    salesPrice: 'Sales Price(₦)',
-    marketPrice: 'Market Price(₦)',
+    standardPrice: "Standard price($)",
+    salesPrice: 'DP',
+    marketPrice: 'RP',
     priceBV: 'Price BV',
     inventory: 'Inventory',
     productDetails: 'Product details',
@@ -841,6 +856,9 @@ export default {
     pleaseSelectPcNo: 'Please select Period No.',
     modifyOrderPcNoHits: 'Do you want to modify the order period No.?',
     consignee: 'consignee',
+    availableCountries: "Available countries",
+    modifyAvailableCountries: "Modify available countries",
+    imagePreview: 'Image Preview'
   },
 
   // Administrator management
@@ -872,4 +890,14 @@ export default {
     addAdmin: 'Add administrator',
     delNotice: 'Determine to delete the selected data'
   },
+  exchangeRateConfig: {
+    currencyType: "Currency Type",
+    ProductExchangeRate: 'Product Exchange Rate',
+    BonusExchangeRate: "Bonus Exchange Rate"
+  },
+  transportationConfig: {
+    countryName: "Country",
+    free: 'Free',
+    freeShipping: "Free shipping threshold"
+  },
 }

+ 40 - 14
src/lang/zh.js

@@ -79,8 +79,10 @@ export default {
     financeRecharge:'充值管理',
     rechargeStateManagement:'状态管理',
     financeWithdraw:'提现管理',
-    atlasRelationOpt:'开拓网络',
+    atlasRelationOpt:'推荐网络',
     atlasNetworkOpt:'安置网络',
+    sponsorNetworkEx:'推荐网络 Ex',
+    placementNetworkEx:'安置网络 Ex',
     shopIndex:'商品列表',
     shopGoodsAdd:'商品添加',
     shopOrderDecList:'外部商城报单列表',
@@ -93,7 +95,7 @@ export default {
     modifyStockistLevel:'修改报单中心级别',
     metaPeriod:'封期管理',
     metaBalanceList:'会员账户余额',
-    metaOtherPeriodBonus:'往期奖金',
+    metaOtherPeriodBonus:'奖金列表',
     metaFlowBonus:'奖金流水',
     metaUserPerf:'用户业绩',
     metaPerfOrder:'业绩单',
@@ -206,14 +208,14 @@ export default {
     RMarketBalancePerformance: '二市场结余业绩',
     confirmAndReviseMemberPerformance: '确认修改会员业绩?',
     periodNo: '期数',
-    bonusMonth: '所在结算月',
+    bonusMonth: '结算月',
     timeframe: '时间范围',
     closingTime: '封期时间',
     timeOfGeneratingPerformanceSheet: '生成业绩单时间',
     settlementDate: '结算时间',
     networkConnectionTime: '挂网时间',
     closurePeriod: '封期',
-    logRefresh: '日志刷新',
+    logRefresh: '日志',
     actionTime: '操作时间(WAT)',
     logContent: '日志内容',
     autoCalculation: '自动计算',
@@ -221,7 +223,7 @@ export default {
     initializeBasicData: '初始化基础数据',
     generationPeriodPerformance: '生成期业绩',
     pullPeriodPerformance: '拉取期业绩',
-    calculationOfBonus: '计算奖金',
+    calculationOfBonus: '计算',
     pullBonusData: '拉取奖金数据',
     bulkWithdrawal: '批量提现',
     periodNum: '期数',
@@ -399,7 +401,8 @@ export default {
     hideSelectedData: '您确定要隐藏所选数据吗?',
     unHideSelectedData: '您确定要展示所选数据吗?',
     exportCurrentData: '您确定要导出当前数据吗?',
-    all: '全部'
+    all: '全部',
+    language: "语言"
   },
 
   filter: {
@@ -475,12 +478,14 @@ export default {
     modifyStockistLevel: '修改报单中心级别',
     memberHighestEmpLevelAdjustment:'调整最高总监级别',
     // 二级菜单/接口-networkChart
+    sponsorNetwork: '推荐网络',
+    sponsorNetworkEx: '推荐网络 Ex',
     placementNetwork: '安置网络',
-    sponsorNetwork: '推荐网路',
+    placementNetworkEx: '安置网络 Ex',
     // 二级菜单/接口-bonusManagement
     closurePeriod: '封期管理',
     memberBonusBalance: '会员账户余额',
-    memberBonus: '往期奖金',
+    memberBonus: '奖金列表',
     bonusFlow: '奖金流水',
     memberPerformance: '用户业绩',
     salesRecord: '业绩单',
@@ -517,7 +522,8 @@ export default {
     otherConfig: '其他配置',
     decLevelConfig: '会员级别配置',
     empLevelConfig: '会员聘级配置',
-    decRoleConfig: '报单中心级别配置'
+    decRoleConfig: '报单中心级别配置',
+    transportationConfig: "运费配置",
   },
 
   // 会员
@@ -528,7 +534,7 @@ export default {
     pleaseInputMemberCode: '请输入会员编号',
     pleaseSelectDirectorLevel: '请选择总监级别',
     modifyHighestDirectorLevelHits: '是否要修改当前会员最高总监级别?',
-    modifyHighestDirectorLevelLevel: '调整会员最高总监级别',
+    modifyHighestDirectorLevelLevel: '调整总监级别',
     selectDirectorLevelHint: '请选择总监级别',
     modifyMemberDirectorLevelHits: '是否要修改当前会员总监级别',
     currentLevel: '当前级别',
@@ -627,7 +633,9 @@ export default {
     paymentPassword: '支付密码',
     selectMemberData: '请勾选要操作的会员',
     sureWant: '确定要对所选会员',
-    exportDataHint: '确定要导出所选数据?'
+    exportDataHint: '确定要导出所选数据?',
+    modifyCountry: "是否修改国家?"
+
   },
 
   // 网络
@@ -684,7 +692,10 @@ export default {
     memberAbout: '会员相关',
     homeAbout: '前台相关',
     withdrawalAbout: '提现相关',
-    otherAbout: '其他'
+    otherAbout: '其他',
+    modifyShippingRates: '编辑运费',
+    modifyExchangeRate: '编辑汇率',
+    refreshProductPricePrompt: '开启后根据新产品汇率刷新上架商品价格'
   },
 
   // 财务
@@ -806,9 +817,11 @@ export default {
     productCode: '商品编号',
     unit: '单位',
     taxRate: '税率',
+    enterTaxRate: '税率必填',
     uSPrice: '美元价格($)',
-    salesPrice: '销售价格(₦)',
-    marketPrice: '市场价格(₦)',
+    standardPrice: "标准价格($)",
+    salesPrice: 'DP',
+    marketPrice: 'RP',
     priceBV: '价格BV',
     inventory: '库存',
     productDetails: '商品详情',
@@ -833,6 +846,9 @@ export default {
     afterAdjustmentPcNo: '调整后期数',
     pleaseSelectPcNo: '请选择要调整到的期数',
     modifyOrderPcNoHits: '是否要修改当前订单期数?',
+    availableCountries: "可购买国家",
+    modifyAvailableCountries: "修改可购买国家",
+    imagePreview: '图片预览'
   },
 
   // 管理员管理
@@ -864,4 +880,14 @@ export default {
     addAdmin: '添加管理员',
     delNotice: '确定删除选定的数据?'
   },
+  exchangeRateConfig: {
+    currencyType: "货币类型",
+    ProductExchangeRate: '产品汇率',
+    BonusExchangeRate: "奖金汇率"
+  },
+  transportationConfig: {
+    countryName: "国家",
+    free: '运费',
+    freeShipping: "免运费阈值"
+  },
 }

+ 22 - 6
src/router/index.js

@@ -7,9 +7,10 @@ Vue.use(Router)
 import Layout from '@/layout'
 
 /* Router Modules */
-import memberRouter from '@/router/modules/member'
 import configRouter from '@/router/modules/config'
-
+import memberRouter from '@/router/modules/member'
+import currencyRouter from "@/router/modules/currency"
+import transportationRouter from "@/router/modules/transportation"
 /**
  * Note: sub-menu only appear when route children.length >= 1
  * Detail see: https://panjiachen.github.io/vue-element-admin-site/guide/essentials/router-and-nav.html
@@ -165,6 +166,18 @@ export const constantRoutes = [
         name: 'atlas_network-opt',
         meta: {title: 'atlasNetworkOpt',},
       },
+      {
+        path: '/atlas/relation-list', // 开拓网络Ex
+        component: () => import('@/views/atlas/relation-ex'),
+        name: 'atlas_relation-ex',
+        meta: {title: 'sponsorNetworkEx',},
+      },
+      {
+        path: '/atlas/network-list', // 安置网络Ex
+        component: () => import('@/views/atlas/network-ex'),
+        name: 'atlas_network-ex',
+        meta: {title: 'placementNetworkEx',},
+      },
     ]
   },
   {
@@ -283,11 +296,14 @@ export const constantRoutes = [
  */
 export const asyncRoutes = [
   /** when your routing map is too long, you can split it into small modules **/
-  // 会员
-  memberRouter,
-  // 设置
-  configRouter,
+    // 会员
+    memberRouter,
+    // 设置
+    configRouter,
+
+    currencyRouter,
 
+    transportationRouter,
   {
     path: '/ad',
     component: Layout,

+ 2 - 2
src/router/modules/config.js

@@ -15,10 +15,10 @@ const configRouter = {
       meta: { title: '站点配置', icon: 'user', noCache: true }
     },
     {
-      path: 'exchange-rate', // 汇率配置
+      path: 'currency-conversions', // 汇率配置
       component: () => import('@/views/config/exchange-rate'),
       name: 'exchangeRate',
-      meta: { title: '汇率配置', icon: 'user', noCache: true }
+      meta: { title: '汇率列表', icon: 'user', noCache: true }
     },
     {
       path: 'dec-role', // 报单中心配置

+ 20 - 0
src/router/modules/currency.js

@@ -0,0 +1,20 @@
+/** When your routing table is too long, you can split it into small modules **/
+
+import Layout from '@/layout'
+
+const currencyRouter = {
+  path: '/currency',
+  component: Layout,
+  redirect: '/currency/currencies-conversions',
+  hidden: true,
+  children: [
+    {
+      path: 'currencies-conversions', // 汇率配置
+      component: () => import('@/views/config/exchange-rate'),
+      name: 'exchangeRate',
+      meta: { title: '汇率列表', icon: 'user', noCache: true }
+    }
+  ]
+}
+
+export default currencyRouter

+ 6 - 0
src/router/modules/member.js

@@ -36,6 +36,12 @@ const memberRouter = {
       name: 'decLevelList',
       meta: { title: 'memberLevelAdjustmentList', icon: 'el-icon-user-solid' }
     },
+    {
+      path: 'change-user-dec-role', // 修改报单中心级别
+      component: () => import('@/views/user/modify-stockist-level'),
+      name: 'modifyStockistLevel',
+      meta: { title: 'modifyStockistLevel', icon: 'el-icon-user-solid' }
+    },
     {
       path: 'change-highest-emp-level-list', // 调整会员最高聘级
       component: () => import('@/views/user/change-highest-emp-level'),

+ 21 - 0
src/router/modules/transportation.js

@@ -0,0 +1,21 @@
+/** When your routing table is too long, you can split it into small modules **/
+
+import Layout from '@/layout'
+
+const transportationRouter = {
+  path: '/transportation',
+  component: Layout,
+  redirect: '/transportation/transportation',
+  hidden: true,
+  children: [
+    {
+      path: 'transportation',
+      component: () => import('@/views/config/transportation-config'),
+      name: 'transportation',
+      meta: { title: '运费配置', icon: 'user', noCache: true }
+    },
+
+  ]
+}
+
+export default transportationRouter

+ 23 - 0
src/styles/element-ui.scss

@@ -82,3 +82,26 @@
 .el-range-separator {
   box-sizing: content-box;
 }
+input[type=number] {
+  -moz-appearance:textfield;
+}
+input[type=number]::-webkit-inner-spin-button,
+input[type=number]::-webkit-outer-spin-button {
+  -webkit-appearance: none;
+  margin: 0;
+}
+.el-input-number .el-input__suffix .el-input-number__decrease,
+.el-input-number .el-input__suffix .el-input-number__increase {
+  display: none;
+}
+.flexJfAc{
+  display: flex;
+  justify-content: flex-start;
+  align-items: center;
+
+}
+.el-form-item__label{
+  word-wrap:break-word;
+  word-break: break-word;
+  overflow-wrap: break-word;
+}

+ 6 - 0
src/styles/index.scss

@@ -189,3 +189,9 @@ aside {
 .multiselect--active {
   z-index: 1000 !important;
 }
+
+.break-word{
+  word-wrap:break-word;
+  word-break: break-word;
+  overflow-wrap: break-word;
+}

+ 1 - 1
src/styles/style.scss

@@ -592,4 +592,4 @@ td .cell .no-border{font-size: 14px;color: #666; font-family: Menlo,Monaco,Conso
 .el-dropdown-menu__item{white-space: nowrap}
 .form-page{width: 650px;}
 .form-page .el-form-item > .el-form-item__content > .el-input,.form-page .el-form-item > .el-form-item__content > .el-cascader,.form-page .el-form-item > .el-form-item__content > .el-select,.form-page .el-form-item > .el-form-item__content > .el-textarea,.form-page .el-form-item > .el-form-item__content > .el-slider{width: 400px;}
-.form-dialog .el-form-item > .el-form-item__content > .el-input,.form-dialog .el-form-item > .el-form-item__content > .el-cascader,.form-dialog .el-form-item > .el-form-item__content > .el-select,.form-dialog .el-form-item > .el-form-item__content > .el-textarea,.form-dialog .el-form-item > .el-form-item__content > .el-slider{width: 350px;}
+.form-dialog .el-form-item > .el-form-item__content > .el-input,.form-dialog .el-form-item > .el-form-item__content > .el-cascader,.form-dialog .el-form-item > .el-form-item__content > .el-select,.form-dialog .el-form-item > .el-form-item__content > .el-textarea,.form-dialog .el-form-item > .el-form-item__content > .el-slider{width: 350px;}

+ 409 - 0
src/views/atlas/network-ex.vue

@@ -0,0 +1,409 @@
+<template>
+    <div v-loading="loading">
+      <div class="white-box">
+        <el-tabs v-model="tabActiveName">
+          <el-tab-pane :label="$t('atlas.resettlementNetworkDiagram')" name="first" v-if="permission.hasPermission(`atlas/network`)"><!-- 安置网络图 -->
+            <div class="filter-user" @keyup.enter="enterToGetData()">
+              <el-input v-model="mainUserName" size="small" style="width:300px;">
+                <template slot="prepend">{{ $t('atlas.topMember') }}<!-- 顶级会员 --></template>
+              </el-input>
+              <el-input v-model="expandDeep" size="small" style="width:200px;">
+                <template slot="prepend">{{ $t('atlas.spreadDepth') }}<!-- 展开深度 --></template>
+              </el-input>
+              <el-input v-model="periodNum" size="small" style="width:150px;" v-show="false">
+                <template slot="prepend">{{ $t('atlas.numberOfPeriods') }}<!-- 期数 --></template>
+              </el-input>
+              <el-button type="primary" size="small" @click="getMainData()">{{ $t('common.confirm') }}<!-- 确定 --></el-button>
+            </div>
+<!--            <el-tree :props="props" :data="treeData" node-key="USER_ID" @node-click="getChildData" ref="tree" :indent="0"-->
+<!--                     default-expand-all :height="tool.getTableHeight(true)">-->
+<!--              <span :id="'node_'+data.USER_ID" :class="'custom-tree-node '+data.className"-->
+<!--                    slot-scope="{ node, data }">-->
+<!--                <span :class="'el-icon-loading '+ data.displayNone"></span>-->
+<!--                <span :class="data.icon"></span>-->
+<!--                <span>-->
+<!--                  <el-tag type="danger">{{ $t('atlas.numberOfLayers') }} :{{countTopDeep(data.TOP_NETWORK_DEEP,topDeep)}}</el-tag>-->
+<!--                  <el-tag>&lt;!&ndash; 会员编号 &ndash;&gt;{{ $t('atlas.memberCode') }}:{{ node.label }}</el-tag>-->
+<!--                  <el-tag>{{ $t('atlas.name') }}&lt;!&ndash; 姓名 &ndash;&gt;:{{data.REAL_NAME}}</el-tag>-->
+<!--                  <el-tag type="danger">&lt;!&ndash; 区位 &ndash;&gt;{{ $t('atlas.location') }}:{{data.RELATIVE_LOCATION}}</el-tag>-->
+<!--                  <el-tag type="success">&lt;!&ndash; 级别 &ndash;&gt;{{ $t('atlas.level') }}:{{data.DEC_LV_NAME}}</el-tag>-->
+<!--                  <el-tag type="warning">&lt;!&ndash; 聘级 &ndash;&gt;{{ $t('atlas.highest') }}:{{data.EMP_LV_NAME}}, {{data.CROWN_LV_NAME}}</el-tag>-->
+<!--                  <el-tag>&lt;!&ndash; 加入期数 &ndash;&gt;{{ $t('atlas.joiningPeriod') }}:{{data.PERIOD_AT}}</el-tag>-->
+<!--                </span>-->
+<!--              </span>-->
+<!--            </el-tree>-->
+            <div v-if="treeChartShow" class="tree-chart">
+              <tree-chart :tree-data="treeData" :top-deep="topDeep" type="network" @clickNode="getNodeData" :clickNodeList="clickNodeList" />
+            </div>
+          </el-tab-pane>
+          <el-tab-pane :label="$t('atlas.placementNetworkList')" name="two" v-if="permission.hasPermission(`atlas/network-list`)"><!-- 安置网络列表 -->
+            <div class="filter-user">
+              <el-input v-model="filterForm.userName" size="small" style="width:300px;">
+                <template slot="prepend"><!-- 会员编号 -->{{ $t('atlas.memberCode') }}</template>
+              </el-input>
+              <el-input v-model="filterForm.deep" size="small" style="width:150px;">
+                <template slot="prepend"><!-- 深度 -->{{ $t('atlas.depth') }}</template>
+              </el-input>
+              <el-input v-model="filterForm.periodNum" size="small" style="width:150px;" v-show="false">
+                <template slot="prepend"><!-- 期数 -->{{ $t('atlas.numberOfPeriods') }}</template>
+              </el-input>
+              <el-button type="primary" size="small" @click="handleFilter">{{ $t('common.confirm') }}<!-- 确定 --></el-button>
+            </div>
+
+            <el-table :data="tableData" stripe style="width: 100%;" :height="tool.getTableHeight(true)">
+              <el-table-column v-for="(tableHeader, key) in tableHeaders" :key="key" :label="tableHeader.header" :width="tableHeader.other.width ? tableHeader.other.width : ''">
+                <template slot-scope="scope">
+                  <template v-if="scope.row[tableHeader.index].other.tag" >
+                    <el-tag :type="scope.row[tableHeader.index].other.tag.type ? scope.row[tableHeader.index].other.tag.type : null" :size="scope.row[tableHeader.index].other.tag.size ? scope.row[tableHeader.index].other.tag.size : null" :class="scope.row[tableHeader.index].other.tag.class ? scope.row[tableHeader.index].other.tag.class : null" >{{scope.row[tableHeader.index].value}}</el-tag>
+                  </template>
+                  <template v-else-if="scope.row[tableHeader.index].other.progress" >
+                    <el-progress type="circle" :percentage="Number.parseInt(percentList['MOVE_PERCENT'][scope.row.ID])"
+                                 :width="50"
+                                 :stroke-width="3"></el-progress>
+                  </template>
+                  <template v-else>
+                    <template v-if="tableHeader.index === 'USER_NAME'">
+                      <el-tooltip class="item" effect="dark" :content="$t('atlas.subordinatesMember')" placement="top"><!-- 查看该会员的下级 -->
+                      <el-button @click.native="handleShow(scope.row)" size="small" type="primary">
+                        {{scope.row[tableHeader.index].value}}
+                      </el-button>
+                      </el-tooltip>
+                    </template>
+                    <template v-else>
+                    <div v-html="scope.row[tableHeader.index].value"></div>
+                    </template>
+                  </template>
+                </template>
+              </el-table-column>
+            </el-table>
+            <div class="white-box-footer">
+              <el-button type="success" size="small" @click="handleExport"
+                         v-show="permission.hasPermission(`atlas/network-list-export`)">{{ $t('common.exportExcel') }}<!-- 导出Excel -->
+              </el-button>
+              <pagination :total="totalCount" :page_size="pageSize" @size-change="handleSizeChange" @current-change="handleCurrentChange"></pagination>
+            </div>
+          </el-tab-pane>
+        </el-tabs>
+      </div>
+    </div>
+  </template>
+
+  <script>
+  import { getNetwork, mainUserInfo, networkList, networkListExport } from '@/api/atlas'
+import Pagination from '@/components/Pagination'
+import baseInfo from '@/utils/baseInfo'
+import permission from '@/utils/permission'
+import tool from '@/utils/tool'
+import store from '@/utils/vuexStore'
+  import treeChart from "@/components/TreeChart/index.vue";
+
+  export default {
+    name: 'atlas_network-ex',
+    components: {treeChart, Pagination},
+    mounted() {
+      this.getData()
+      store.state.socket.onMessageCallback = this.onMessageCallback
+    },
+    data() {
+      return {
+        loading: true,
+        tabActiveName: 'first',
+        permission: permission,
+        //relation
+        props: {
+          label: 'USER_NAME',
+          children: 'children',
+          //isLeaf: 'leaf',
+          icon: 'icon',
+        },
+        // treeData: null,
+        expandDeep: 2,
+        topDeep: 0,
+        mainUserName: '',
+        periodNum: null,
+        listPeriodNum: null,
+        allData: null,
+        tableHeaders: null,
+        tableData: null,
+        currentPage: 1,
+        totalPages: 1,
+        totalCount: 1,
+        pageSize: 20,
+        tool: tool,
+        filterForm: {
+          userName: null,
+          deep: 2,
+          periodNum: null,
+        },
+        listTopDeep: 0,
+        //tree
+        treeChartShow: false,
+        treeData: {
+          name: null,
+          children: null
+        },
+        clickNodeList:[],
+        clickNodeUserId:null
+      }
+    },
+    methods: {
+      //tree
+      getNodeData(e) {
+        // console.log(e)
+        if (e.collapsed === false && e.data.children == null) {
+          console.log(e.name + '---节点展开')
+        }
+        if (e.data.children == null) {
+          this.clickNodeUserId = e.data.USER_ID
+          this.getChildData(e.data, 'clickNode')
+        }
+      },
+      getData() {
+        this.$message({
+          message: this.$t('atlas.getDataWait'), // 正在获取数据,请稍后
+        })
+        this.periodNum = baseInfo.nowPeriodNum()
+        this.filterForm.periodNum = baseInfo.nowPeriodNum()
+        if (permission.hasPermission(`atlas/network-list`) && !permission.hasPermission(`atlas/network`)) {
+          this.tabActiveName = 'two'
+          this.getListData()
+          console.log(this.treeData)
+        }
+        if (permission.hasPermission(`atlas/network`)) {
+          this.tabActiveName = 'first'
+          if(permission.hasPermission(`atlas/network-list`)){
+            this.getMainData(null,true)
+          }else{
+            this.getMainData()
+          }
+        }
+      },
+      getMainData (userName = null, getList = false) {
+        this.$message.closeAll()
+        this.$message({
+          message: this.$t('atlas.getDataWait'),//正在获取数据,请稍后
+        })
+        let thisObj = this
+        let requestData = {
+          periodNum: this.periodNum
+        }
+        if (this.mainUserName !== null) {
+          requestData = {userName: this.mainUserName,periodNum: this.periodNum}
+        }
+        mainUserInfo(requestData).then(response=>{
+            const treeData = Object.assign(response.data[0])
+            thisObj.treeData = {...treeData}
+            thisObj.topDeep = Number(response.data[0].TOP_NETWORK_DEEP)
+            thisObj.listPeriodNum = response.data[0].listPeriodNum
+            if(getList) thisObj.getListData()
+            this.getChildData(treeData)
+
+            thisObj.loading = false
+            thisObj.treeChartShow = false
+
+        }).catch(err => {
+            this.$message({
+                message: err,
+                type: 'error'
+            })
+            thisObj.loading = false
+        })
+      },
+      getChildData (data, type) {
+        let thisObj = this
+        let userId = data.USER_ID
+        let thisData = data
+        if (thisData.leaf) {
+          return ''
+        }
+        if (thisData.isExpanded) {
+          return ''
+        }
+        if (thisData.children !== null && thisData.children.length > 0) {
+          return ''
+        }
+        thisData.displayNone = ''
+        getNetwork({
+          id: userId,
+          deep: thisObj.expandDeep,
+          periodNum: this.periodNum
+        }).then(response => {
+            // thisObj.$refs.tree.updateKeyChildren(userId, response.data.allData)
+            thisObj.listPeriodNum = response.data.periodNum
+            thisData.displayNone = 'display-none'
+            thisData.isExpanded = true
+            const resAllData = response.data.allData
+
+          if (type === 'clickNode') {
+            console.log('clickNode', thisData.USER_ID)
+            thisObj.clickNodeList = resAllData
+            thisObj.treeData.children = thisObj.treeAddNode(thisObj.treeData.children)
+          } else {
+            thisObj.treeData.children = thisObj.traversalTree(resAllData, thisObj)
+          }
+
+          thisObj.treeChartShow = true
+          thisObj.loading = false
+        }).catch(err => {
+            this.$message({
+              message: err,
+              type: 'error'
+            })
+        })
+      },
+      traversalTree(tree) {
+        const that = this
+        tree.forEach((item) => {
+          item.name = item.USER_NAME
+          if (item.children && item.children.length > 0) {
+            that.traversalTree(item.children)
+          }
+        })
+        return tree
+      },
+      treeAddNode(tree) {
+        const that = this
+        tree.forEach((item) => {
+          if (item.USER_ID == that.clickNodeUserId) {
+            item.children = this.clickNodeList
+          }
+          if (item.children && item.children.length > 0) {
+            that.treeAddNode(item.children)
+          }
+        })
+        return tree
+      },
+      countTopDeep(deep,topDeep){
+        return Number(deep)-Number(topDeep)
+      },
+      handleCurrentChange (page) {
+        this.getListData(page, this.pageSize)
+      },
+      handleSizeChange (pageSize) {
+        this.getListData(this.currentPage, pageSize)
+      },
+      handleFilter () {
+        this.getListData(1, this.pageSize)
+        this.tabActiveName = 'two'
+      },
+      handleShow(row) {
+        this.loading = true
+        this.filterForm.userName = row.SEE_USER_NAME
+        this.getListData(1, this.pageSize)
+        this.tabActiveName = 'two'
+      },
+      getListData (page, pageSize) {
+        let obj = this
+        let filterData = this.filterForm
+        const paramsData = Object.assign({
+            page: (page === null || page == undefined) ? 1 : page,
+            pageSize: (pageSize === null || pageSize == undefined) ? obj.pageSize : pageSize
+          }, filterData)
+        networkList(paramsData).then(response => {
+            obj.tableHeaders = response.data.columnsShow ? response.data.columnsShow : []
+            obj.tableData = response.data.list
+            obj.filterTypes = response.data.filterTypes
+            obj.allData = response.data.list
+            obj.listTopDeep = response.data.listTopDeep
+            obj.periodNum = response.data.periodNum
+            obj.currentPage = page
+            obj.totalPages = parseInt(response.data.totalPages)
+            obj.totalCount = parseInt(response.data.totalCount)
+            obj.pageSize = pageSize
+            obj.loading = false
+        }).catch(err => {
+            obj.loading = false
+            this.$message({
+              message: err,
+              type: 'error'
+            })
+        })
+      },
+      onMessageCallback(data) {
+        //this.getData(this.currentPage, this.pageSize, false)
+      },
+      handleExport(){
+        this.$confirm(this.$t('atlas.exportNotice'), this.$t('common.hint'), {//`确定要导出当前数据吗?`, '提示'
+          confirmButtonText: this.$t('common.confirm'), // 确定
+          cancelButtonText: this.$t('common.cancel'), // 取消
+          type: 'warning'
+        }).then(() => {
+            networkListExport(this.filterForm).then(response => {
+                this.$message({
+                    message: response.data,
+                    type: 'success'
+                })
+            }).catch(err => {
+                this.$message({
+                message: err,
+                type: 'error'
+                })
+            })
+        })
+      },
+      enterToGetData (ev) {
+        this.getMainData()
+      },
+    }
+  }
+  </script>
+
+  <style>
+    .filter-user{font-size: 14px;margin-bottom: 5px;}
+    .filter-user:after{content: '';display: table;
+      clear: both;}
+    .filter-user .el-input-group{float: left;margin-right: 15px;}
+    .el-tree {
+      padding-bottom: 20px;
+      font-size: 14px;
+      overflow-x: auto;
+    }
+
+    .el-tree .el-tag {
+      height: 20px;
+      line-height: 18px;vertical-align: middle;
+    }
+    .el-tree-node{position: relative;}
+
+    .el-tree-node__content {
+      height: 30px;
+      line-height: 30px;
+    }
+
+    .el-tree-node__children {
+      position: relative;
+      padding: 0 0 0 16px;
+    }
+
+    .el-tree-node:before {
+      position: absolute;
+      content: '';
+      top: 0px;
+      left: -4px;
+      height: 100%;
+      border-left: 1px solid #ccc;
+    }
+    .el-tree-node:last-child:before{height: 15px;}
+
+    .custom-tree-node {
+      position: relative;
+      padding-left: 5px;
+    }
+
+    .first-node:before {
+      display: none;
+    }
+
+    .custom-tree-node:before {
+      position: absolute;
+      width: 8px;
+      content: '';
+      top: 15px;
+      left: -4px;
+      border-bottom: 1px solid #ccc;
+    }
+
+    .el-tree-node__expand-icon {
+      display: none !important;
+    }
+  </style>

+ 412 - 0
src/views/atlas/relation-ex.vue

@@ -0,0 +1,412 @@
+<template>
+    <div v-loading="loading">
+      <div class="white-box">
+        <el-tabs v-model="tabActiveName">
+          <el-tab-pane :label="$t('atlas.sponsorNetworkDiagram')" name="first" v-if="permission.hasPermission(`atlas/relation`)"><!-- 开拓网络图 -->
+            <div class="filter-user">
+              <el-input v-model="mainUserName" size="small" style="width:300px;">
+                <template slot="prepend">{{ $t('atlas.topMember') }}<!-- 顶级会员 --></template>
+              </el-input>
+              <el-input v-model="expandDeep" size="small" style="width:200px;">
+                <template slot="prepend">{{ $t('atlas.spreadDepth') }}<!-- 展开深度 --></template>
+              </el-input>
+              <el-input v-model="periodNum" size="small" style="width:150px;" v-show="false">
+                <template slot="prepend">{{ $t('atlas.numberOfPeriods') }}<!-- 期数 --></template>
+              </el-input>
+              <el-button type="primary" size="small" @click="getMainData()">{{ $t('common.confirm') }}<!-- 确定 --></el-button>
+            </div>
+<!--            <el-tree :props="props" :data="treeData" node-key="USER_ID" @node-click="getChildData" ref="tree" :indent="0"-->
+<!--                     default-expand-all>-->
+<!--                  <span :id="'node_'+data.USER_ID" :class="'custom-tree-node '+data.className"-->
+<!--                        slot-scope="{ node, data }">-->
+<!--                      <span :class="'el-icon-loading '+ data.displayNone"></span>-->
+<!--                      <span :class="data.icon"></span>-->
+<!--                      <span>-->
+<!--                        <el-tag type="danger">&lt;!&ndash; 代数 &ndash;&gt;{{ $t('atlas.algebra') }}:  {{countTopDeep(data.TOP_RELATION_DEEP,topDeep)}}</el-tag>-->
+<!--                        <el-tag>&lt;!&ndash; 会员编号 &ndash;&gt;{{ $t('atlas.memberCode') }}:{{ node.label }}</el-tag>-->
+<!--                        <el-tag type="success">{{ $t('atlas.name') }}&lt;!&ndash; 姓名 &ndash;&gt;:{{data.REAL_NAME}}</el-tag>-->
+<!--                        <el-tag type="warning">{{ $t('atlas.level') }}&lt;!&ndash; 级别 &ndash;&gt;:{{data.DEC_LV_NAME}}</el-tag>-->
+<!--                        <el-tag type="warning">{{ $t('atlas.highest') }}:&lt;!&ndash; 聘级 &ndash;&gt;{{data.EMP_LV_NAME}}, {{data.CROWN_LV_NAME}}</el-tag>-->
+<!--  &lt;!&ndash;                      <el-tag type="warning">Star:&lt;!&ndash; 星级 &ndash;&gt;{{data.CROWN_LV_NAME}}</el-tag>&ndash;&gt;-->
+<!--                      </span>-->
+<!--                  </span>-->
+<!--            </el-tree>-->
+            <div v-if="treeChartShow" class="tree-chart">
+              <tree-chart :tree-data="treeData" :top-deep="topDeep" type="relation" @clickNode="getNodeData" :clickNodeList="clickNodeList" />
+            </div>
+          </el-tab-pane>
+<!--          <el-tab-pane :label="$t('atlas.sponsorNetworkList')" name="two" v-if="permission.hasPermission(`atlas/relation-list`)">-->
+<!--            <div class="filter-user">-->
+<!--              <el-input v-model="filterForm.userName" size="small" style="width:300px;">-->
+<!--                <template slot="prepend">{{ $t('atlas.memberCode') }}</template>-->
+<!--              </el-input>-->
+<!--              <el-input v-model="filterForm.deep" size="small" style="width:150px;">-->
+<!--                <template slot="prepend">{{ $t('atlas.depth') }}</template>-->
+<!--              </el-input>-->
+<!--              <el-input v-model="filterForm.periodNum" size="small" style="width:150px;" v-show="false">-->
+<!--                <template slot="prepend">{{ $t('atlas.numberOfPeriods') }}</template>-->
+<!--              </el-input>-->
+<!--              <el-button type="primary" size="small" @click="handleFilter">{{ $t('common.confirm') }}</el-button>-->
+<!--            </div>-->
+
+<!--            <el-table :data="tableData" stripe style="width: 100%;" :height="tool.getTableHeight(true)">-->
+<!--              &lt;!&ndash;<el-table-column type="selection" width="55" v-if="tableHeaders"></el-table-column>&ndash;&gt;-->
+<!--              <el-table-column v-for="(tableHeader, key) in tableHeaders" :key="key" :label="tableHeader.header" :width="tableHeader.other.width ? tableHeader.other.width : ''">-->
+<!--                <template slot-scope="scope">-->
+<!--                  <template v-if="scope.row[tableHeader.index].other.tag" >-->
+<!--                    <el-tag :type="scope.row[tableHeader.index].other.tag.type ? scope.row[tableHeader.index].other.tag.type : null" :size="scope.row[tableHeader.index].other.tag.size ? scope.row[tableHeader.index].other.tag.size : null" :class="scope.row[tableHeader.index].other.tag.class ? scope.row[tableHeader.index].other.tag.class : null" >{{scope.row[tableHeader.index].value}}</el-tag>-->
+<!--                  </template>-->
+<!--                  <template v-else-if="scope.row[tableHeader.index].other.progress" >-->
+<!--                    <el-progress type="circle" :percentage="Number.parseInt(percentList['MOVE_PERCENT'][scope.row.ID])"-->
+<!--                                 :width="50"-->
+<!--                                 :stroke-width="3"></el-progress>-->
+<!--                  </template>-->
+<!--                  <template v-else>-->
+<!--                    <template v-if="tableHeader.index === 'USER_NAME'">-->
+<!--                      <el-tooltip class="item" effect="dark" :content="$t('atlas.subordinatesMember')" placement="top">-->
+<!--                        <el-button @click.native="handleShow(scope.row)" size="small" type="primary">-->
+<!--                          {{scope.row[tableHeader.index].value}}-->
+<!--                        </el-button>-->
+<!--                      </el-tooltip>-->
+<!--                    </template>-->
+<!--                    <template v-else>-->
+<!--                      <div v-html="scope.row[tableHeader.index].value"></div>-->
+<!--                    </template>-->
+<!--                  </template>-->
+<!--                </template>-->
+<!--              </el-table-column>-->
+<!--            </el-table>-->
+<!--            <div class="white-box-footer">-->
+<!--              <el-button type="success" size="small" @click="handleExport"-->
+<!--                         v-show="permission.hasPermission(`atlas/relation-list-export`)">{{ $t('common.exportExcel') }}&lt;!&ndash; 导出Excel &ndash;&gt;-->
+<!--              </el-button>-->
+<!--              <pagination :total="totalCount" :page_size="pageSize" @size-change="handleSizeChange" @current-change="handleCurrentChange"></pagination>-->
+<!--            </div>-->
+<!--          </el-tab-pane>-->
+        </el-tabs>
+      </div>
+    </div>
+  </template>
+
+  <script>
+  import { getRelation, mainUserInfo, relationList, relationListExport } from '@/api/atlas'
+  import Pagination from '@/components/Pagination'
+  import treeChart from '@/components/TreeChart/index.vue'
+  import baseInfo from '@/utils/baseInfo'
+  import permission from '@/utils/permission'
+  import tool from '@/utils/tool'
+  import store from '@/utils/vuexStore'
+
+    export default {
+      name: 'atlas_relation-ex',
+      components: {Pagination,treeChart},
+      mounted() {
+        this.getData()
+        store.state.socket.onMessageCallback = this.onMessageCallback
+      },
+      data() {
+        return {
+          loading: true,
+          tabActiveName: 'first',
+          permission: permission,
+          //relation
+          props: {
+            label: 'USER_NAME',
+            children: 'children',
+            //isLeaf: 'leaf',
+            icon: 'icon',
+          },
+          // treeData: null,
+          expandDeep: 2,
+          topDeep: 0,
+          mainUserName: '',
+          periodNum: null,
+          listPeriodNum: null,
+          allData: null,
+          tableHeaders: null,
+          tableData: null,
+          currentPage: 1,
+          totalPages: 1,
+          totalCount: 1,
+          pageSize: 20,
+          tool: tool,
+          filterForm: {
+            userName: null,
+            deep: 2,
+            periodNum: null,
+          },
+          listTopDeep: 0,
+          //tree
+          treeChartShow: false,
+          treeData: {
+            name: null,
+            children: null
+          },
+          clickNodeList:[],
+          clickNodeUserId:null
+        }
+      },
+      methods: {
+        //tree
+        getNodeData(e) {
+          // console.log(e)
+          if (e.collapsed === false && e.data.children == null) {
+            console.log(e.name + '---节点展开')
+          }
+          if (e.data.children == null) {
+            this.clickNodeUserId = e.data.USER_ID
+            this.getChildData(e.data, 'clickNode')
+          }
+        },
+        getData() {
+          this.$message({
+            message: this.$t('atlas.getDataWait'),//正在获取数据,请稍后
+            duration: 0
+          })
+          this.periodNum = baseInfo.nowPeriodNum()
+          this.filterForm.periodNum = baseInfo.nowPeriodNum()
+          if (permission.hasPermission(`atlas/relation-list`) && !permission.hasPermission(`atlas/relation`)) {
+            this.tabActiveName = 'two'
+            this.getListData()
+            console.log(this.treeData)
+          }
+          if (permission.hasPermission(`atlas/relation`)) {
+            this.tabActiveName = 'first'
+            if (permission.hasPermission(`atlas/relation-list`)) {
+              this.getMainData(null, true)
+            } else {
+              this.getMainData()
+            }
+          }
+        },
+        getMainData (userName = null, getList = false) {
+          this.$message.closeAll()
+          this.$message({
+            message: this.$t('atlas.getDataWait'),//正在获取数据,请稍后
+          })
+          let thisObj = this
+          let requestData = {
+            periodNum: this.periodNum
+          }
+          if (this.mainUserName !== null) {
+            requestData = {userName: this.mainUserName,periodNum: this.periodNum}
+          }
+          mainUserInfo(requestData).then(response=>{
+            const treeData = Object.assign(response.data[0])
+            thisObj.treeData = {...treeData}
+            thisObj.topDeep = Number(response.data[0].TOP_RELATION_DEEP)
+            thisObj.listPeriodNum = response.data[0].listPeriodNum
+            if(getList) thisObj.getListData()
+            this.getChildData(treeData)
+            thisObj.loading = false
+            thisObj.treeChartShow = false
+
+          }).catch(err => {
+            this.$message({
+                message: err,
+                type: 'error'
+            })
+            thisObj.loading = false
+          })
+        },
+        getChildData (data, type) {
+          let thisObj = this
+          thisObj.loading = true
+
+          let userId = data.USER_ID
+          let thisData = data
+          if (thisData.leaf) {
+            return ''
+          }
+          if (thisData.isExpanded) {
+            return ''
+          }
+          if (thisData.children !== null && thisData.children.length > 0) {
+            return ''
+          }
+          // this.$message({
+          //   message: this.$t('atlas.getDataWait'),//正在获取数据,请稍后
+          // })
+          thisData.displayNone = ''
+          getRelation({
+            id: userId,
+            deep: thisObj.expandDeep,
+            periodNum: this.periodNum
+          }).then(response => {
+            // thisObj.$refs.tree.updateKeyChildren(userId, response.data.allData)
+            thisObj.listPeriodNum = response.data.periodNum
+            thisObj.displayNone = 'display-none'
+            thisObj.isExpanded = true
+            const resAllData = response.data.allData
+
+            if (type === 'clickNode') {
+              console.log('clickNode', thisData.USER_ID)
+              thisObj.clickNodeList = resAllData
+              thisObj.treeData.children = thisObj.treeAddNode(thisObj.treeData.children)
+            } else {
+              thisObj.treeData.children = thisObj.traversalTree(resAllData, thisObj)
+            }
+
+            thisObj.treeChartShow = true
+            thisObj.loading = false
+          }).catch(err => {
+            thisObj.$message({
+                message: err,
+                type: 'error'
+            })
+          })
+        },
+        traversalTree(tree) {
+          const that = this
+          tree.forEach((item) => {
+            item.name = item.USER_NAME
+            if (item.children && item.children.length > 0) {
+              that.traversalTree(item.children)
+            }
+          })
+          return tree
+        },
+        treeAddNode(tree) {
+          const that = this
+          tree.forEach((item) => {
+            if (item.USER_ID == that.clickNodeUserId) {
+              item.children = this.clickNodeList
+            }
+            if (item.children && item.children.length > 0) {
+              that.treeAddNode(item.children)
+            }
+          })
+          return tree
+        },
+        countTopDeep(deep,topDeep){
+          return Number(deep)-Number(topDeep)
+        },
+        getListData(page, pageSize) {
+          let obj = this
+          let filterData = this.filterForm
+          const paramsData = Object.assign({
+            page: (page === null || page == undefined) ? 1 : page,
+            pageSize: (pageSize === null || pageSize == undefined) ? obj.pageSize : pageSize
+          }, filterData)
+          relationList(paramsData).then(response => {
+            obj.tableHeaders = response.data.columnsShow ? response.data.columnsShow : []
+            obj.tableData = response.data.list
+            obj.filterTypes = response.data.filterTypes
+            obj.allData = response.data.list
+            obj.listTopDeep = response.data.listTopDeep
+            obj.periodNum = response.data.periodNum
+            obj.currentPage = page
+            obj.totalPages = parseInt(response.data.totalPages)
+            obj.totalCount = parseInt(response.data.totalCount)
+            obj.pageSize = pageSize
+            obj.loading = false
+          }).catch(err => {
+            obj.loading = false
+            this.$message({
+                message: err,
+                type: 'error'
+            })
+          })
+        },
+        handleCurrentChange(page) {
+          this.getListData(page, this.pageSize)
+        },
+        handleSizeChange(pageSize) {
+          this.getListData(this.currentPage, pageSize)
+        },
+        handleFilter() {
+          this.getListData(1, this.pageSize)
+          this.tabActiveName = 'two'
+        },
+        handleShow(row) {
+          this.loading = true
+          this.filterForm.userName = row.SEE_USER_NAME
+          this.filterForm.deep = 1
+          this.getListData(1, this.pageSize)
+          this.tabActiveName = 'two'
+        },
+        onMessageCallback(data) {
+          //this.getData(this.currentPage, this.pageSize, false)
+        },
+        handleExport(){
+          this.$confirm(this.$t('atlas.exportNotice'), this.$t('common.hint'), {
+            confirmButtonText: this.$t('common.confirm'), // 确定
+            cancelButtonText: this.$t('common.cancel'), // 取消
+            type: 'warning'
+          }).then(() => {
+            relationListExport(this.filterForm).then(response => {
+                this.$message({
+                   message: response.data,
+                   type: 'success'
+                })
+            }).catch(err => {
+                this.$message({
+                   message: err,
+                   type: 'error'
+                })
+            })
+          })
+        },
+      }
+    }
+  </script>
+
+  <style>
+    .filter-user{font-size: 14px;margin-bottom: 5px;}
+    .filter-user:after{content: '';display: table;
+      clear: both;}
+    .filter-user .el-input-group{float: left;margin-right: 15px;}
+    .el-tree {
+      padding-bottom: 20px;
+      font-size: 14px;
+      overflow-x: auto;
+    }
+
+    .el-tree .el-tag {
+      height: 20px;
+      line-height: 18px;vertical-align: middle;
+    }
+    .el-tree-node{position: relative;}
+
+    .el-tree-node__content {
+      height: 30px;
+      line-height: 30px;
+    }
+
+    .el-tree-node__children {
+      position: relative;
+      padding: 0 0 0 16px;
+    }
+
+    .el-tree-node:before {
+      position: absolute;
+      content: '';
+      top: 0px;
+      left: -4px;
+      height: 100%;
+      border-left: 1px solid #ccc;
+    }
+    .el-tree-node:last-child:before{height: 15px;}
+
+    .custom-tree-node {
+      position: relative;
+      padding-left: 5px;
+    }
+
+    .first-node:before {
+      display: none;
+    }
+
+    .custom-tree-node:before {
+      position: absolute;
+      width: 8px;
+      content: '';
+      top: 15px;
+      left: -4px;
+      border-bottom: 1px solid #ccc;
+    }
+
+    .el-tree-node__expand-icon {
+      display: none !important;
+    }
+  </style>

+ 13 - 15
src/views/atlas/relation-opt.vue

@@ -26,8 +26,7 @@
                         <el-tag><!-- 会员编号 -->{{ $t('atlas.memberCode') }}:{{ node.label }}</el-tag>
                         <el-tag type="success">{{ $t('atlas.name') }}<!-- 姓名 -->:{{data.REAL_NAME}}</el-tag>
                         <el-tag type="warning">{{ $t('atlas.level') }}<!-- 级别 -->:{{data.DEC_LV_NAME}}</el-tag>
-                        <el-tag type="warning">{{ $t('atlas.highest') }}:<!-- 聘级 -->{{data.EMP_LV_NAME}}, {{data.CROWN_LV_NAME}}</el-tag>
-  <!--                      <el-tag type="warning">Star:&lt;!&ndash; 星级 &ndash;&gt;{{data.CROWN_LV_NAME}}</el-tag>-->
+                        <el-tag type="warning">{{ $t('atlas.highest') }}:<!-- 聘级 -->{{data.EMP_LV_NAME}}</el-tag>
                       </span>
                   </span>
             </el-tree>
@@ -45,7 +44,7 @@
               </el-input>
               <el-button type="primary" size="small" @click="handleFilter">{{ $t('common.confirm') }}<!-- 确定 --></el-button>
             </div>
-  
+
             <el-table :data="tableData" stripe style="width: 100%;" :height="tool.getTableHeight(true)">
               <!--<el-table-column type="selection" width="55" v-if="tableHeaders"></el-table-column>-->
               <el-table-column v-for="(tableHeader, key) in tableHeaders" :key="key" :label="tableHeader.header" :width="tableHeader.other.width ? tableHeader.other.width : ''">
@@ -84,7 +83,7 @@
       </div>
     </div>
   </template>
-  
+
   <script>
     import tool from '@/utils/tool'
     import store from '@/utils/vuexStore'
@@ -92,7 +91,7 @@
     import Pagination from '@/components/Pagination'
     import baseInfo from '@/utils/baseInfo'
     import { mainUserInfo, getRelation, relationList, relationListExport } from '@/api/atlas'
-  
+
     export default {
       name: 'atlas_relation-opt',
       components: {Pagination},
@@ -286,7 +285,7 @@
       }
     }
   </script>
-  
+
   <style>
     .filter-user{font-size: 14px;margin-bottom: 20px;}
     .filter-user:after{content: '';display: table;
@@ -297,23 +296,23 @@
       font-size: 14px;
       overflow-x: auto;
     }
-  
+
     .el-tree .el-tag {
       height: 20px;
       line-height: 18px;vertical-align: middle;
     }
     .el-tree-node{position: relative;}
-  
+
     .el-tree-node__content {
       height: 30px;
       line-height: 30px;
     }
-  
+
     .el-tree-node__children {
       position: relative;
       padding: 0 0 0 16px;
     }
-  
+
     .el-tree-node:before {
       position: absolute;
       content: '';
@@ -323,16 +322,16 @@
       border-left: 1px solid #ccc;
     }
     .el-tree-node:last-child:before{height: 15px;}
-  
+
     .custom-tree-node {
       position: relative;
       padding-left: 5px;
     }
-  
+
     .first-node:before {
       display: none;
     }
-  
+
     .custom-tree-node:before {
       position: absolute;
       width: 8px;
@@ -341,9 +340,8 @@
       left: -4px;
       border-bottom: 1px solid #ccc;
     }
-  
+
     .el-tree-node__expand-icon {
       display: none !important;
     }
   </style>
-  

+ 33 - 140
src/views/bonus/period.vue

@@ -7,146 +7,41 @@
       </div>
       <el-table :data="tableData" stripe style="width: 100%;" @selection-change="handleSelectionChange"
                 :height="tool.getTableHeight()">
-        <el-table-column prop="PERIOD_NUM" :label="$t('bonus.periodNo')" width="100"><!-- 期数 -->
+        <el-table-column prop="PERIOD_NUM" :label="$t('bonus.periodNo')"><!-- 期数 -->
           <template slot-scope="scope">
             <el-tag type="" size="small" class="no-border">{{scope.row.PERIOD_NUM}}</el-tag>
           </template>
         </el-table-column>
-        <el-table-column :label="$t('bonus.bonusMonth')" width="110"><!-- 所在结算月 -->
+
+        <el-table-column :label="$t('bonus.bonusMonth')"><!-- 结算月 -->
           <template slot-scope="scope">
-            <el-tag type="warning" size="small" class="no-border">{{scope.row.CALC_YEAR}}-{{scope.row.CALC_MONTH}}
+            <el-tag type="warning" size="small" class="no-border">{{ scope.row.CALC_YEAR_MONTH }}
             </el-tag>
           </template>
         </el-table-column>
-        <el-table-column :label="$t('bonus.timeframe')" width="260"><!-- 时间范围 -->
-          <template slot-scope="scope">
-            <!-- 应开始于 -->{{ $t('bonus.shouldStartOn') }}:{{getWatTime(scope.row.START_TIME)}}<br/>
-            <!-- 应结束于 -->{{ $t('bonus.shouldEndOn') }}:{{getWatTime(scope.row.END_TIME)}}
-          </template>
-        </el-table-column>
-        <el-table-column :label="$t('bonus.closingTime')" width="180"><!-- 封期时间 -->
-          <template slot-scope="scope">
-            {{getWatTime(scope.row.CLOSED_AT)}}
-          </template>
-        </el-table-column>
-<!--        <el-table-column label="Performance sheet progress" width="90">&lt;!&ndash; 业绩单进度 &ndash;&gt;-->
-<!--          <template slot-scope="scope">-->
-<!--            <el-progress type="circle" :percentage="Number.parseInt(percentList['PERF_PERCENT'][scope.row.ID])"-->
-<!--                         :width="50" :stroke-width="3"></el-progress>-->
-<!--          </template>-->
-<!--        </el-table-column>-->
-        <el-table-column :label="$t('bonus.timeOfGeneratingPerformanceSheet')" width="230"><!-- 生成业绩单时间 -->
-          <template slot-scope="scope">
-            <!-- 开始 -->{{ $t('bonus.start') }}:{{getWatTime(scope.row.PERF_STARTED_AT)}}<br>
-            <!-- 完成 -->{{ $t('bonus.complete') }}:{{getWatTime(scope.row.PERFED_AT)}}
-          </template>
-        </el-table-column>
-<!--        <el-table-column label="Settlement progress" width="80">&lt;!&ndash; 结算进度 &ndash;&gt;-->
-<!--          <template slot-scope="scope">-->
-<!--            <el-progress type="circle" :percentage="Number.parseInt(percentList['CALC_PERCENT'][scope.row.ID])"-->
-<!--                         :width="50" :stroke-width="3"></el-progress>-->
-<!--          </template>-->
-<!--        </el-table-column>-->
-        <el-table-column :label="$t('bonus.settlementDate')" width="230"><!-- 结算时间 -->
+
+        <el-table-column :label="$t('bonus.settlementDate')"><!-- 结算时间 -->
           <template slot-scope="scope">
-            <!-- 开始 -->{{ $t('bonus.start') }}:{{getWatTime(scope.row.CALCULATE_STARTED_AT)}}<br>
-            <!-- 完成 -->{{ $t('bonus.complete') }}:{{getWatTime(scope.row.CALCULATED_AT)}}
+            {{ getWatTime(scope.row.CALCULATED_AT) }}
           </template>
         </el-table-column>
-<!--        <el-table-column label="Network connection progress" width="80">&lt;!&ndash; 挂网进度 &ndash;&gt;-->
-<!--          <template slot-scope="scope">-->
-<!--            <el-progress type="circle" :percentage="Number.parseInt(percentList['SENT_PERCENT'][scope.row.ID])"-->
-<!--                         :width="50" :stroke-width="3"></el-progress>-->
-<!--          </template>-->
-<!--        </el-table-column>-->
-        <el-table-column :label="$t('bonus.networkConnectionTime')" width="230"><!-- 挂网时间 -->
+
+        <el-table-column :label="$t('bonus.networkConnectionTime')"><!-- 挂网时间 -->
           <template slot-scope="scope">
-            <!-- 开始 -->{{ $t('bonus.start') }}:{{getWatTime(scope.row.SEND_STARTED_AT)}}<br/>
-            <!-- 完成 -->{{ $t('bonus.complete') }}:{{getWatTime(scope.row.SENT_AT)}}
+            {{getWatTime(scope.row.SENT_AT)}}
           </template>
         </el-table-column>
-        <el-table-column :fixed="fixed" :label="$t('common.action')" width=""><!-- 操作 -->
+        <el-table-column :fixed="fixed" :label="$t('common.action')"><!-- 操作 -->
           <template slot-scope="scope">
-<!--            <el-button v-if="scope.row.IS_CAN_CLOSE && permission.hasPermission(`bonus/close-period`)" class="button" type="primary"-->
-<!--                       @click.native="trialCalcHandle(scope.row.PERIOD_NUM)" >-->
-<!--              计算-->
-<!--            </el-button>-->
-            <el-button v-if="scope.row.IS_CAN_CLOSE || scope.row.IS_CAN_PERF" type="primary" class="button"
-                       @click="dialogTableVisible = true;currentPeriod = scope.row.PERIOD_NUM;getDialogData();">
-              {{$t('common.action')}}</el-button>
-            <el-button style="margin: 0 0 !important;" v-if="scope.row.IS_CAN_CLOSE && permission.hasPermission(`bonus/close-period`)" type="primary" class="button"
-                       @click.native="closeHandle(scope.row)">
-              {{$t('bonus.closurePeriod')}}<!-- 封期 -->
-            </el-button>
-            <el-button style="margin: 0 0 !important;" @click.native="sentHandle(scope.row)" type="primary" class="button"
-                              v-if="scope.row.IS_CAN_SENT && permission.hasPermission(`bonus/send-period`)">
-              <!-- 挂网 -->{{ $t('bonus.spreadNet') }}
-            </el-button>
-
-            <el-dialog :title="$t('bonus.logTitle', { currentPeriod: currentPeriod })" :visible.sync="dialogTableVisible" append-to-body :width="screenWidth">
-              <el-row>
-                <el-button style="margin-left:10px;" class="button" type="primary" @click.native="getDialogData();dialogLoading=true;" >
-                  {{$t('bonus.logRefresh')}}
-                </el-button>
-              </el-row>
-              <el-divider></el-divider>
-
-              <el-row>
-                <el-button  class="button customstyle" style="margin-left:10px;" type="success" @click.native="autoCalcHandle(currentPeriod)"
-                  v-if="permission.hasPermission(`bonus/calc-period`)"
-                >{{$t('bonus.autoCalculation')}}</el-button>
-                <el-button   class="button customstyle" type="primary" @click.native="perfOrderHandle(currentPeriod)" >{{$t('bonus.createPerformanceSheet')}}</el-button>
-                <el-button  class="button customstyle" type="primary" @click.native="initDataHandle(currentPeriod)" >{{$t('bonus.initializeBasicData')}}</el-button>
-                <el-button  class="button customstyle" type="primary" @click.native="perfPeriodHandle(currentPeriod)" >{{$t('bonus.generationPeriodPerformance')}}</el-button>
-                <el-button  class="button customstyle" type="primary" @click.native="pullPerfPeriodHandle(currentPeriod)" >{{$t('bonus.pullPeriodPerformance')}}</el-button>
-                <el-button  class="button customstyle" type="primary" @click.native="calcBonusHandle(currentPeriod)" >{{$t('bonus.calculationOfBonus')}}</el-button>
-                <el-button  class="button customstyle" type="primary" @click.native="pullBonusHandle(currentPeriod)" >{{$t('bonus.pullBonusData')}}</el-button>
-              </el-row>
 
-<!--                  <el-row><el-button class="button" type="primary" @click.native="autoPerfHandle(currentPeriod)" >计算和拉取期业绩</el-button></el-row>-->
-                <div>
-                  <el-table :data="dialogData" height="550" v-loading="dialogLoading" style="width: 100%">
-                    <el-table-column prop="CREATED_AT" :label="$t('bonus.actionTime')" width="180">
-                      <template slot-scope="scope">
-                        {{getWatTime(scope.row.CREATED_AT)}}
-                      </template>
-                    </el-table-column>
-                    <el-table-column prop="TEXT" :label="$t('bonus.logContent')" width="650"></el-table-column>
-                  </el-table>
-              </div>
-            </el-dialog>
-
-<!--            <el-dropdown size="small" trigger="click" v-if=false>-->
-<!--            <el-dropdown size="small" trigger="click" v-if="scope.row.BUTTON_IS_CAN">-->
-<!--              <el-button type="primary" size="small" @click.stop="">-->
-<!--                &lt;!&ndash; 操作该数据 &ndash;&gt;Action<i class="el-icon-arrow-down el-icon&#45;&#45;right"></i>-->
-<!--              </el-button>-->
-<!--              <el-dropdown-menu slot="dropdown">-->
-<!--                <el-dropdown-item @click.native="closeHandle(scope.row)"-->
-<!--                                  v-if="scope.row.IS_CAN_CLOSE && permission.hasPermission(`bonus/close-period`)">Closure period&lt;!&ndash; 封期 &ndash;&gt;-->
-<!--                </el-dropdown-item>-->
-<!--                <el-dropdown-item @click.native="perfHandle(scope.row)"-->
-<!--                                  v-if="scope.row.IS_PERFING === '0' && scope.row.IS_CAN_PERF && permission.hasPermission(`bonus/perf-period`)">-->
-<!--                  &lt;!&ndash; 生成业绩单 &ndash;&gt;Generate performance sheet-->
-<!--                </el-dropdown-item>-->
-<!--                <el-dropdown-item @click.native="perfHandle(scope.row)"-->
-<!--                                  v-if="scope.row.IS_PERFING === '1' && scope.row.IS_CAN_PERF && permission.hasPermission(`bonus/perf-period`)">-->
-<!--                  &lt;!&ndash; 强制生成业绩单 &ndash;&gt;Forced generation of performance sheet-->
-<!--                </el-dropdown-item>-->
-<!--                <el-dropdown-item @click.native="calcHandle(scope.row)"-->
-<!--                                  v-if="scope.row.IS_CALCING === '0' && scope.row.IS_CAN_CALC && permission.hasPermission(`bonus/calc-period`)">-->
-<!--                  &lt;!&ndash; 结算 &ndash;&gt;settlement-->
-<!--                </el-dropdown-item>-->
-<!--                <el-dropdown-item @click.native="calcHandle(scope.row)"-->
-<!--                                  v-if="scope.row.IS_CALCING === '1' && scope.row.IS_CAN_CALC && permission.hasPermission(`bonus/calc-period`)">-->
-<!--                  &lt;!&ndash; 强制结算 &ndash;&gt;Forced settlement-->
-<!--                </el-dropdown-item>-->
-<!--                <el-dropdown-item @click.native="sentHandle(scope.row)"-->
-<!--                                  v-if="scope.row.IS_CAN_SENT && permission.hasPermission(`bonus/send-period`)">-->
-<!--                  &lt;!&ndash; 挂网 &ndash;&gt;spread a net-->
-<!--                </el-dropdown-item>-->
-<!--              </el-dropdown-menu>-->
-<!--            </el-dropdown>-->
+            <!-- 计算 -->
+            <el-button v-if="scope.row.IS_CAN_CALC" class="button" type="primary" size="small" @click.native="calcHandle(scope.row.PERIOD_NUM)">{{ $t('bonus.calculationOfBonus') }}</el-button>
+            <!-- 挂网 -->
+            <el-button @click.native="sentHandle(scope.row)" type="danger" class="button" size="small" v-if="scope.row.IS_CAN_SENT && permission.hasPermission(`bonus/send-period`)">
+              {{ $t('bonus.spreadNet') }}
+            </el-button>
+            <!-- 日志 -->
+            <el-button v-if="scope.row.IS_SHOW_LOG" class="button" type="info" size="small" @click.native="getDialogData(scope.row.PERIOD_NUM)" >{{$t('bonus.logRefresh')}}</el-button>
           </template>
         </el-table-column>
       </el-table>
@@ -157,6 +52,14 @@
         <pagination :total="totalCount" :page_size="pageSize" @size-change="handleSizeChange" @current-change="handleCurrentChange"></pagination>
       </div>
     </div>
+
+    <el-dialog title="dialog" :visible.sync="dialogLoading" top="0">
+      <el-table :data="dialogData" stripe size="small">
+        <el-table-column property="CREATE_TIME" label="日期"></el-table-column>
+        <el-table-column property="TEXT" label="内容"></el-table-column>
+      </el-table>
+    </el-dialog>
+
   </div>
 </template>
 
@@ -211,15 +114,6 @@ export default {
         'periodNum': {isUserTable: false, name: this.$t('bonus.periodNum')}, // 期数
         'year': {isUserTable: false, name: this.$t('bonus.year')}, // 所在结算年
         'month': {isUserTable: false, name: this.$t('bonus.month')}, // 所在结算月
-        'startTime': {isUserTable: false, name: this.$t('bonus.startTime'), other: 'date'}, // 期数开始时间
-        'endTime': {isUserTable: false, name: this.$t('bonus.endTime'), other: 'date'}, // 期数结束时间
-        'closedAt': {isUserTable: false, name: this.$t('bonus.closedAt'), other: 'date'}, // 封期时间
-        'perfStartedAt': {isUserTable: false, name: this.$t('bonus.perfStartedAt'), other: 'date'}, // 生成业绩单开始时间
-        'perfedAt': {isUserTable: false, name: this.$t('bonus.perfedAt'), other: 'date'}, // 生成业绩单结束时间
-        'calStartedAt': {isUserTable: false, name: this.$t('bonus.calStartedAt'), other: 'date'}, // 结算开始时间
-        'calculatedAt': {isUserTable: false, name: this.$t('bonus.calculatedAt'), other: 'date'}, // 结算结束时间
-        'sendStartedAt': {isUserTable: false, name: this.$t('bonus.sendStartedAt'), other: 'date'}, // 挂网开始时间
-        'sentAt': {isUserTable: false, name: this.$t('bonus.sentAt'), other: 'date'} // 挂网结束时间
       },
       filterModel: {},
       percentList: {
@@ -231,7 +125,7 @@ export default {
       dialogData: null,
       currentDialogPage: 1,
       dialogPageSize: 100,
-      dialogLoading: true,
+      dialogLoading: false,
       form: {
         name: '',
         region: '',
@@ -420,15 +314,14 @@ export default {
     //   })
     // },
 
-    getDialogData () {
-      let vueObj = this
+    getDialogData (period) {
       let paramsData = {
         page: this.currentDialogPage,
         pageSize: this.dialogPageSize,
       }
-      fetchRecordList(this.currentPeriod,paramsData).then(response => {
+      fetchRecordList(period, paramsData).then(response => {
         this.dialogData = response.data.list
-        this.dialogLoading = false;
+        this.dialogLoading = true
       })
     },
 
@@ -451,13 +344,13 @@ export default {
           }
       })
     },
-    calcHandle (row) {
+    calcHandle (period) {
       this.$confirm(this.$t('bonus.performSettlementOperationHint'), this.$t('common.hint'), { // '确定对当前期进行结算操作?', '提示'
         confirmButtonText: this.$t('common.confirm'), // 确定
         cancelButtonText: this.$t('common.cancel'), // 取消
         type: 'warning'
       }).then(() => {
-        return fetchCalcPeriod(row.PERIOD_NUM)
+        return fetchCalcPeriod(period)
       }).then(response => {
         this.$message({
           message: response.data,

+ 7 - 7
src/views/config/bonus-config.vue

@@ -4,7 +4,7 @@
       <div class="panel-heading">{{ $t('config.baseConfig') }}</div>
       <div class="panel-wrapper">
         <div class="panel-body">
-          <el-form ref="form" :model="form" label-width="250px" :label-position="labelPosition">
+          <el-form ref="form" :model="form" label-width="265px" :label-position="labelPosition">
             <template v-for="item in configData">
               <div v-if="item.SORT==='101'" class="hr-tip"><span>{{ $t('config.repeatAbout') }}</span></div>
               <el-form-item :key="item.CONFIG_NAME" :label="item.TITLE">
@@ -67,7 +67,7 @@
       </div>
     </div>
 
-    <div v-if="permission.hasPermission(`config/bonus-dec-level`)" class="panel">
+    <div v-if="false" class="panel">
       <div class="panel-heading">{{ $t('config.teamBonusConfig') }}</div>
       <div class="panel-wrapper">
         <div class="panel-body">
@@ -95,7 +95,7 @@
       </div>
     </div>
 
-    <div v-if="permission.hasPermission(`config/bonus-emp-level`)" class="panel">
+    <div v-if="false" class="panel">
       <div class="panel-heading">{{ $t('config.manageStarConfig') }}</div>
       <div class="panel-wrapper">
         <div class="panel-body">
@@ -145,7 +145,7 @@
       </div>
     </div>
 
-    <div v-if="permission.hasPermission(`config/bonus-star-level`)" class="panel">
+    <div v-if="false" class="panel">
       <div class="panel-heading">{{ $t('config.crownStarConfig') }}</div>
       <div class="panel-wrapper">
         <div class="panel-body">
@@ -243,9 +243,9 @@ export default {
       this.loading = true
       fetchBonusConfig().then(response => {
         this.configData = response.data.config
-        this.decLevelTableData = Object.values(response.data.decLevel)
-        this.empLevelTableData = Object.values(response.data.empLevel)
-        this.crownLevelTableData = Object.values(response.data.crownLevel)
+        // this.decLevelTableData = Object.values(response.data.decLevel)
+        // this.empLevelTableData = Object.values(response.data.empLevel)
+        // this.crownLevelTableData = Object.values(response.data.crownLevel)
 
         setTimeout(() => {
           this.loading = false

+ 97 - 20
src/views/config/exchange-rate.vue

@@ -1,31 +1,63 @@
 <template>
   <div class="app-container">
     <div v-loading="loading" class="white-box">
-      <el-form ref="form" :model="form" label-width="250px" :label-position="labelPosition" class="form-page">
-        <el-form-item :key="form.CONFIG_NAME" :label="form.TITLE">
-          <el-input v-model="form.VALUE" style="max-width: 300px;">
-            <template slot="append">naira / $</template>
-          </el-input>
-        </el-form-item>
-        <el-form-item :label="$t('config.refreshShopPrice')">
-          <el-switch v-model="synchronize" />
-          <el-divider direction="vertical" />
-          <span style="font-weight: bold; color: red; font-size: 14px;">{{ $t('config.refreshShopNaraPriceThenSave') }}</span>
-        </el-form-item>
-        <el-form-item>
-          <el-button type="primary" size="medium" :loading="submitButtonStat" @click="onSubmit">{{ $t('common.confirm') }}</el-button>
+      <el-table :data="conversionsList" stripe style="width: 100%;">
+        <el-table-column prop="NAME" :label="$t('exchangeRateConfig.currencyType')" />
+        <el-table-column prop="PRODUCT_RATE" :label="$t('exchangeRateConfig.ProductExchangeRate')" min-width="90px;" />
+        <el-table-column prop="BONUSES_RATE" :label="$t('exchangeRateConfig.BonusExchangeRate')" min-width="90px;" />
+<!--        <el-table-column :label="$t('common.createdAt')" min-width="100px;">-->
+<!--          <template slot-scope="{row}">-->
+<!--            {{ row.CREATED_AT | parseTime('{y}-{m}-{d} {h}:{i}:{s}') }}-->
+<!--          </template>-->
+<!--        </el-table-column>-->
+<!--        <el-table-column :label="$t('common.updatedAt')" min-width="100px;">-->
+<!--          <template slot-scope="{row}">-->
+<!--            {{ row.UPDATED_AT | parseTime('{y}-{m}-{d} {h}:{i}:{s}') }}-->
+<!--          </template>-->
+<!--        </el-table-column>-->
+        <el-table-column :label="$t('common.action')" min-width="100px;">
+          <template slot-scope="scope">
+            <el-button type="primary" size="mini" icon="el-icon-edit" plain @click="edit(scope.row)">{{ $t('common.edit') }}</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+
+    <!-- 编辑	-->
+    <el-dialog v-loading="editLoading" :title="$t('config.modifyExchangeRate')" :visible.sync="dialog" :width="screenWidth" style="margin-top: -80px">
+      <el-form ref="editForm" :model="editForm" :label-position="labelPosition" label-width="130px" style="width: 100%; margin-top: -30px; margin-bottom: -15px;">
+        <el-row :gutter="3">
+          <el-col :xs="24" :sm="24" :lg="12">
+            <el-input v-show="false" v-model="editForm.ID" size="small" type="text" />
+            <el-form-item :label="$t('exchangeRateConfig.ProductExchangeRate')" prop="Product_Exchange_Rate" style="margin-bottom: 10px; width: 100%;">
+              <el-input-number v-model.trim="editForm.product_rate" :precision="6" :min="0" size="small" type="text" style="min-width: 300px;" readonly />
+            </el-form-item>
+            <el-form-item :label="$t('exchangeRateConfig.BonusExchangeRate')" prop="Bonus_Exchange_Rate" style="margin-bottom: 10px; width: 100%;">
+              <el-input-number v-model="editForm.bonuses_rate" :precision="6" :min="0" size="small" type="text" style="min-width: 300px;" />
+            </el-form-item>
+            <el-form-item :label="$t('config.refreshShopPrice')" style="min-width: 420px;" >
+              <el-switch v-model="editForm.synchronize" /> <div class="synchronize break-word">( {{$t('config.refreshProductPricePrompt')}} )</div>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-form-item style="margin-bottom: 15px;">
+          <el-button type="warning" size="mini" @click="dialog = false">{{ $t('table.cancel') }}</el-button>
+          <el-button type="primary" size="mini" @click="editSubmit">{{ $t('table.confirm') }}</el-button>
         </el-form-item>
       </el-form>
-    </div>
+    </el-dialog>
+
   </div>
 </template>
 
 <script>
-import { fetchExchangeRateConfig, updateExchangeRateConfig } from '@/api/config'
+import {
+  fetchCurrencyConversionsList, setCurrencyConversions, fetchCurrenciesList
+} from '@/api/config'
 import { getScreenWidth } from '@/utils'
 
 export default {
-  name: 'ExchangeRate',
+  name: 'Currencies',
   data() {
     return {
       form: {
@@ -37,7 +69,18 @@ export default {
       submitButtonStat: false,
       synchronize: false,
       screenWidth: getScreenWidth() > 600 ? '500px' : getScreenWidth() + 'px',
-      labelPosition: getScreenWidth() > 600 ? 'right' : 'top'
+      labelPosition: getScreenWidth() > 600 ? 'right' : 'top',
+      conversionsList: [],
+      editLoading: false,
+      editForm: {
+        from_currency_id: '149',
+        to_currency_id: '',
+        product_rate: '',
+        bonuses_rate: '',
+        synchronize: false,
+      },
+      dialog: false,
+
     }
   },
   created() {
@@ -46,14 +89,38 @@ export default {
   methods: {
     fetchData() {
       this.loading = true
-      fetchExchangeRateConfig().then(response => {
-        this.form = response.data
-
+      fetchCurrencyConversionsList().then(response => {
+        this.conversionsList = response
         setTimeout(() => {
           this.loading = false
         }, 0.5 * 1000)
       })
     },
+    edit(data){
+      this.editForm = {
+            from_currency_id: '149',
+            to_currency_id: data.ID,
+            product_rate: data.PRODUCT_RATE,
+            bonuses_rate: data.BONUSES_RATE,
+            synchronize: false,
+      }
+      this.dialog = true
+    },
+    editSubmit(){
+      setCurrencyConversions(this.editForm).then(response => {
+        this.$message({
+          message: response.data,
+          type: 'success'
+        })
+        this.dialog = false
+        this.fetchData()
+      }).catch(error => {
+        this.$message({
+          message: error,
+          type: 'warning'
+        })
+      })
+    },
     onSubmit() {
       this.$confirm(this.$t('config.confirmToChangeExchangeRate'), this.$t('common.hint'), {
         confirmButtonText: this.$t('common.confirm'),
@@ -105,4 +172,14 @@ export default {
 .form-page {
 	width: 100%;
 }
+.synchronize{
+  color: #F56C6C;
+}
+
+::v-deep .el-form-item__label{
+  word-wrap:break-word;
+  word-break: break-word;
+  overflow-wrap: break-word;
+}
+
 </style>

+ 6 - 6
src/views/config/transfer-config.vue

@@ -15,14 +15,14 @@
           <el-table-column :label="$t('config.lowerLimitAmountTransferOut')" min-width="150px">
             <template slot-scope="scope">
               <el-input v-model="scope.row.outMin" size="small" maxlength="16">
-                <template slot="append">₦</template>
+
               </el-input>
             </template>
           </el-table-column>
           <el-table-column :label="$t('config.upperLimitAmountTransferOut')" min-width="150px">
             <template slot-scope="scope">
               <el-input v-model="scope.row.outMax" size="small" maxlength="16">
-                <template slot="append">₦</template>
+
               </el-input>
             </template>
           </el-table-column>
@@ -34,28 +34,28 @@
           <el-table-column :label="$t('config.lowerLimitServiceCharge')" min-width="150px">
             <template slot-scope="scope">
               <el-input v-model="scope.row.feeMin" size="small" maxlength="16">
-                <template slot="append">₦</template>
+
               </el-input>
             </template>
           </el-table-column>
           <el-table-column :label="$t('config.upperLimitServiceCharge')" min-width="150px">
             <template slot-scope="scope">
               <el-input v-model="scope.row.feeMax" size="small" maxlength="16">
-                <template slot="append">₦</template>
+
               </el-input>
             </template>
           </el-table-column>
           <el-table-column :label="$t('config.periodUpperLimitAmountTransferOut')" min-width="150px">
             <template slot-scope="scope">
               <el-input v-model="scope.row.weekMax" size="small" maxlength="16">
-                <template slot="append">₦</template>
+
               </el-input>
             </template>
           </el-table-column>
           <el-table-column :label="$t('config.monthUpperLimitAmountTransferOut')" min-width="150px">
             <template slot-scope="scope">
               <el-input v-model="scope.row.monthMax" size="small" maxlength="16">
-                <template slot="append">₦</template>
+
               </el-input>
             </template>
           </el-table-column>

+ 138 - 0
src/views/config/transportation-config.vue

@@ -0,0 +1,138 @@
+<template>
+  <div class="app-container">
+    <div v-loading="loading" class="white-box">
+      <el-table :data="conversionsList" stripe style="width: 100%;">
+        <el-table-column prop="NAME" :label="$t('transportationConfig.countryName')" />
+        <el-table-column prop="currency.NAME" :label="$t('exchangeRateConfig.currencyType')" />
+        <el-table-column prop="free_shipping" :label="$t('transportationConfig.freeShipping')" min-width="90px;" />
+        <el-table-column prop="freight" :label="$t('transportationConfig.free')" min-width="90px;" />
+<!--        <el-table-column :label="$t('common.createdAt')" min-width="100px;">-->
+<!--          <template slot-scope="{row}">-->
+<!--            {{ row.CREATED_AT | parseTime('{y}-{m}-{d} {h}:{i}:{s}') }}-->
+<!--          </template>-->
+<!--        </el-table-column>-->
+<!--        <el-table-column :label="$t('common.updatedAt')" min-width="100px;">-->
+<!--          <template slot-scope="{row}">-->
+<!--            {{ row.UPDATED_AT | parseTime('{y}-{m}-{d} {h}:{i}:{s}') }}-->
+<!--          </template>-->
+<!--        </el-table-column>-->
+        <el-table-column :label="$t('common.action')" min-width="100px;">
+          <template slot-scope="scope">
+            <el-button type="primary" size="mini" icon="el-icon-edit" plain @click="edit(scope.row)">{{ $t('common.edit') }}</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+
+    <!-- 编辑	-->
+    <el-dialog v-loading="editLoading" :title="$t('config.modifyShippingRates')" :visible.sync="dialog" :width="screenWidth" style="margin-top: -80px">
+      <el-form ref="editForm" :model="editForm" :label-position="labelPosition" label-width="130px" style="width: 100%; margin-top: -30px; margin-bottom: -15px;">
+        <el-row :gutter="3">
+          <el-col :xs="24" :sm="24" :lg="12">
+            <el-input v-show="false" v-model="editForm.ID" size="small" type="text" />
+            <el-form-item :label="$t('transportationConfig.freeShipping')" prop="freeShipping" style="margin-bottom: 10px; width: 100%;">
+              <el-input-number v-model.trim="editForm.freeShipping" :precision="0" :controls="false" :min="0" size="small" style="min-width: 300px;" />
+            </el-form-item>
+            <el-form-item :label="$t('transportationConfig.free')" prop="free" style="margin-bottom: 10px; width: 100%;">
+              <el-input-number v-model.trim="editForm.freight" :precision="0" :controls="false" :min="0" size="small" style="min-width: 300px;" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-form-item style="margin-bottom: 15px;">
+          <el-button type="warning" size="mini" @click="dialog = false">{{ $t('table.cancel') }}</el-button>
+          <el-button type="primary" size="mini" @click="editSubmit">{{ $t('table.confirm') }}</el-button>
+        </el-form-item>
+      </el-form>
+    </el-dialog>
+
+  </div>
+</template>
+
+<script>
+import {
+  fetchTransportationList, setTransportation
+} from '@/api/config'
+import { getScreenWidth } from '@/utils'
+
+export default {
+  name: 'transportation',
+  data() {
+    return {
+      form: {
+        CONFIG_NAME: '',
+        VALUE: 0,
+        TITLE: ''
+      },
+      loading: true,
+      submitButtonStat: false,
+      synchronize: false,
+      screenWidth: getScreenWidth() > 600 ? '500px' : getScreenWidth() + 'px',
+      labelPosition: getScreenWidth() > 600 ? 'right' : 'top',
+      conversionsList: [],
+      editLoading: false,
+      editForm: {
+        countryId: "",
+        freight: "",
+        freeShipping: "",
+      },
+      dialog: false,
+
+    }
+  },
+  created() {
+    this.fetchData()
+  },
+  methods: {
+    fetchData() {
+      this.loading = true
+      fetchTransportationList().then(response => {
+        this.conversionsList = response
+        setTimeout(() => {
+          this.loading = false
+        }, 0.5 * 1000)
+      })
+    },
+    edit(data){
+      this.editForm = {
+            countryId: data.ID,
+            freight: data.freight,
+            freeShipping: data.free_shipping,
+      }
+      this.dialog = true
+    },
+    editSubmit(){
+      setTransportation(this.editForm).then(response => {
+        this.$message({
+          message: response.data,
+          type: 'success'
+        })
+        this.dialog = false
+        this.fetchData()
+      }).catch(error => {
+        this.$message({
+          message: error,
+          type: 'warning'
+        })
+      })
+    },
+  }
+}
+</script>
+
+<style scoped>
+.app-main {
+	padding: 15px;
+}
+.app-container {
+	padding: 0;
+}
+.white-box {
+	padding: 15px;
+}
+.form-page {
+	width: 100%;
+}
+::v-deep .el-input-number .el-input__inner {
+  text-align: left;
+}
+</style>

+ 2 - 2
src/views/dashboard/admin/components/BarChart.vue

@@ -3,9 +3,9 @@
 </template>
 
 <script>
-import echarts from 'echarts'
-require('echarts/theme/macarons') // echarts theme
+import * as echarts from 'echarts'
 import resize from './mixins/resize'
+require('echarts/theme/macarons') // echarts theme
 
 const animationDuration = 6000
 

+ 2 - 2
src/views/dashboard/admin/components/LineChart.vue

@@ -3,9 +3,9 @@
 </template>
 
 <script>
-import echarts from 'echarts'
-require('echarts/theme/macarons') // echarts theme
+import * as echarts from 'echarts'
 import resize from './mixins/resize'
+require('echarts/theme/macarons') // echarts theme
 
 export default {
   mixins: [resize],

+ 2 - 2
src/views/dashboard/admin/components/PieChart.vue

@@ -3,9 +3,9 @@
 </template>
 
 <script>
-import echarts from 'echarts'
-require('echarts/theme/macarons') // echarts theme
+import * as echarts from 'echarts'
 import resize from './mixins/resize'
+require('echarts/theme/macarons') // echarts theme
 
 export default {
   mixins: [resize],

+ 2 - 2
src/views/dashboard/admin/components/RaddarChart.vue

@@ -3,9 +3,9 @@
 </template>
 
 <script>
-import echarts from 'echarts'
-require('echarts/theme/macarons') // echarts theme
+import * as echarts from 'echarts'
 import resize from './mixins/resize'
+require('echarts/theme/macarons') // echarts theme
 
 const animationDuration = 3000
 

+ 428 - 32
src/views/shop/goods-add.vue

@@ -32,23 +32,26 @@
                 <el-form-item :label="$t('shop.unit')"><!-- 单位 -->
                     <el-input v-model="form.unit"></el-input>
                 </el-form-item>
-                <el-form-item :label="$t('shop.taxRate')"><!-- 税率 -->
-                  <el-input v-model="form.taxRate">
-                    <template slot="append">%</template>
-                  </el-input>
-                </el-form-item>
-                <el-form-item :label="$t('shop.uSPrice')" p>
-                    <el-input v-model="form.sellPriceStandard"></el-input>
-                </el-form-item>
-                <el-form-item :label="$t('shop.salesPrice')">
-                    <el-input v-model="form.sellPrice"></el-input>
-                </el-form-item>
-                <el-form-item :label="$t('shop.marketPrice')">
-                  <el-input v-model="form.marketPrice"></el-input>
+<!--                <el-form-item :label="$t('shop.taxRate')">&lt;!&ndash; 税率 &ndash;&gt;-->
+<!--                  <el-input v-model="form.taxRate">-->
+<!--                    <template slot="append">%</template>-->
+<!--                  </el-input>-->
+<!--                </el-form-item>-->
+                <el-form-item :label="$t('shop.standardPrice')">
+                    <el-input-number v-model="form.sellPriceStandard" @input="handleInput" class="left-aligned-input" :precision="2" :step="0.1" :min="0" :controls="false" style="text-align: left"></el-input-number>
                 </el-form-item>
+<!--                <el-form-item :label="$t('shop.salesPrice')">-->
+<!--                    <el-input v-model="form.sellPrice"></el-input>-->
+<!--                </el-form-item>-->
+<!--                <el-form-item :label="$t('shop.marketPrice')">-->
+<!--                  <el-input v-model="form.marketPrice"></el-input>-->
+<!--                </el-form-item>-->
                 <el-form-item :label="$t('shop.priceBV')" v-show="pvDisabled"><!-- 价格BV -->
                     <el-input v-model="form.pricePv"></el-input>
                 </el-form-item>
+                <el-form-item :label="$t('shop.availableCountries')"><!-- 可购买国家 -->
+                  <el-button type="primary" size="small" :disabled="!form.sellPriceStandard" @click="editAvailableCountries">{{ $t('shop.modifyAvailableCountries') }}</el-button>
+                </el-form-item>
                 <el-form-item :label="$t('shop.inventory')"><!-- 库存 -->
                     <el-input v-model="form.storeNums"></el-input>
                 </el-form-item>
@@ -82,11 +85,105 @@
                 </el-form-item>
             </el-form>
         </div>
+      <el-dialog :title="$t('shop.modifyAvailableCountries')" :visible.sync="editAvailableCountriesDialog" top="3%" width="60%"><!-- 修改可购买国家 -->
+        <el-form ref="form"  label-width="130px" class="">
+          <el-form-item :label="$t('shop.productName')"><!-- 商品名称 -->
+            <el-input v-model="form.goodsName" disabled></el-input>
+          </el-form-item>
+          <el-form-item :label="$t('transportationConfig.countryName')">
+            <el-input
+              v-model="searchQuery"
+              placeholder=""
+              @input="updateTableData"
+            ></el-input>
+          </el-form-item>
+        </el-form>
+
+        <el-form :model="availableCountriesTableForm" ref="availableCountriesTableForm" :rules="rules">
+          <el-table v-show="!searchQuery" :data="availableCountriesTableForm.availableCountriesList" ref="multipleTable" @row-click="handleRowClick" @selection-change="handleSelectionChange" @select="selectOne" :selectable="isRowSelectable" :header-cell-class-name="cellClass" stripe height="500" style="width: 100%;">
+            <el-table-column
+                type="selection"
+                width="55"
+                :selectable="selectable"
+                :show-select-all="false"
+            >
+            </el-table-column>
+            <el-table-column prop="NAME" :label="$t('transportationConfig.countryName')" />
+            <el-table-column prop="LOCAL_CURRENCY_NAME" :label="$t('exchangeRateConfig.currencyType')" />
+            <el-table-column :label="$t('shop.salesPrice')" min-width="100px;">
+              <template slot-scope="scope">
+                <el-form-item :prop="`availableCountriesList.${scope.$index}.sellPrice`" >
+                  <el-input-number v-model="scope.row.sellPrice" :controls="false"></el-input-number>
+                </el-form-item>
+              </template>
+            </el-table-column>
+            <el-table-column :label="$t('shop.marketPrice')" min-width="100px;">
+              <template slot-scope="scope">
+                <el-form-item :prop="`availableCountriesList.${scope.$index}.marketPrice`">
+                  <el-input-number v-model="scope.row.marketPrice" :precision="2" :min="0" :controls="false" clearable></el-input-number>
+                </el-form-item>
+              </template>
+            </el-table-column>
+            <el-table-column :label="$t('shop.taxRate')" min-width="100px;">
+              <template slot-scope="scope">
+                  <el-form-item :prop="`availableCountriesList.${scope.$index}.taxRate`" >
+                    <div class="flexJfAc">
+                      <el-input-number v-model="scope.row.taxRate" :precision="2" :step="0.1" :min="0" :controls="false"></el-input-number><span>%</span>
+                    </div>
+                  </el-form-item>
+              </template>
+            </el-table-column>
+          </el-table>
+
+          <el-table v-show="searchQuery" :data="availableCountriesTableForm.tempData" ref="multipleTableTemp" @row-click="handleRowClickTemp" @selection-change="handleSelectionChangeTemp" @select="selectOneTemp" :selectable="isRowSelectable" :header-cell-class-name="cellClass" stripe height="500" style="width: 100%;">
+            <el-table-column
+                type="selection"
+                width="55"
+                :selectable="selectable"
+                :show-select-all="false"
+            >
+            </el-table-column>
+            <el-table-column prop="NAME" :label="$t('transportationConfig.countryName')" />
+            <el-table-column prop="LOCAL_CURRENCY_NAME" :label="$t('exchangeRateConfig.currencyType')" />
+            <el-table-column :label="$t('shop.salesPrice')" min-width="100px;">
+              <template slot-scope="scope">
+                <el-form-item :prop="`tempData.${scope.$index}.sellPrice`" >
+                  <el-input-number v-model="scope.row.sellPrice" :controls="false" disabled></el-input-number>
+                </el-form-item>
+              </template>
+            </el-table-column>
+            <el-table-column :label="$t('shop.marketPrice')" min-width="100px;">
+              <template slot-scope="scope">
+                <el-form-item :prop="`tempData.${scope.$index}.marketPrice`">
+                  <el-input-number v-model="scope.row.marketPrice" :precision="2" :min="0" :controls="false" clearable></el-input-number>
+                </el-form-item>
+              </template>
+            </el-table-column>
+            <el-table-column :label="$t('shop.taxRate')" min-width="100px;">
+              <template slot-scope="scope">
+                <el-form-item :prop="`tempData.${scope.$index}.taxRate`" >
+                  <div class="flexJfAc">
+                    <el-input-number v-model="scope.row.taxRate" :precision="2" :step="0.1" :min="0" :controls="false"></el-input-number><span>%</span>
+                  </div>
+                </el-form-item>
+              </template>
+            </el-table-column>
+          </el-table>
+
+        </el-form>
+
+        <div slot="footer" class="dialog-footer">
+          <el-button type="warning" size="mini" @click="editAvailableCountriesDialog = false">{{ $t('table.cancel') }}</el-button>
+          <el-button type="primary" size="mini" @click="submitAvailableCountries">{{ $t('table.confirm') }}</el-button>
+        </div>
+
+      </el-dialog>
     </div>
 </template>
 
 <script>
 import { addGoods, getAddGoodsConfig, getUploadToken } from '@/api/shop'
+import { getCountries } from "@/api/site"
 import Upload from '@/components/Upload'
 import baseInfo from '@/utils/baseInfo'
 
@@ -134,8 +231,9 @@ export default {
         textarea: '',
         sellDiscount: '',
         categoryType: '',
-        sellPriceStandard: '',
-        taxRate: 0
+        sellPriceStandard: undefined,
+        taxRate: 0,
+        nature: []
       },
       submitButtonStat: false,
       goodsType: [],
@@ -145,7 +243,22 @@ export default {
       width: '100px',
       height: '100px',
       pvDisabled: true,
-      exchangeRate: baseInfo.exchangeRate()
+      exchangeRate: baseInfo.exchangeRate(),
+      editAvailableCountriesDialog: false,
+      availableCountriesTableForm: {
+        availableCountriesList: [],
+        tempData:[],
+      },
+      multipleSelection: [],
+      multipleSelectionTemp: [],
+
+      rules: {},
+      rulesTemp: {},
+      searchQuery: '',
+
+      selectionTempData: [],
+      selectedIds: [], // 存储选中的项的 ID
+      selectedIdsTemp: [],
     }
   },
   watch: {
@@ -158,16 +271,251 @@ export default {
       handler (modern, origin) {
         this.pvDisabled = (parseInt(modern) === 1)
       }
-    }
+    },
     // 监听商品标准价格,自动计算销售价格
-    // 'form.sellPriceStandard': {
-    //     deep: true,
-    //     handler(modern, origin) {
-    //         this.form.sellPrice = modern * this.exchangeRate
-    //     }
-    // },
+    'form.sellPriceStandard': {
+        deep: true,
+        handler(modern, origin) {
+            // this.form.sellPrice = modern * this.exchangeRate
+          if(modern){
+            this.form.nature = []
+            this.availableCountriesTableForm.availableCountriesList = []
+            this.toggleSelection(this.form.nature)
+          }
+        }
+    },
+  },
+  computed: {
+    filteredData() {
+      if (!this.searchQuery) {
+        return this.availableCountriesTableForm.availableCountriesList;
+      }
+      const query = this.searchQuery.toLowerCase();
+      // this.$refs.multipleTable.toggleRowSelection(JSON.parse(JSON.stringify(this.multipleSelection)),true)
+      return this.availableCountriesTableForm.availableCountriesList.filter(row => {
+        return row.NAME.toLowerCase().includes(query);
+      });
+
+    }
   },
   methods: {
+    updateTableData() {
+      // if (!this.searchQuery) {
+      //   return this.availableCountriesTableForm.availableCountriesList;
+      // }
+      // 临时存储原始数据,保持搜索时不改变其他列的值
+      const query = this.searchQuery.toLowerCase();
+      // this.$refs.multipleTable.toggleRowSelection(JSON.parse(JSON.stringify(this.multipleSelection)),true)
+
+      if (this.availableCountriesTableForm.tempData.length === 0) {
+        // this.availableCountriesTableForm.tempData = JSON.parse(JSON.stringify(this.availableCountriesTableForm.availableCountriesList));
+        // this.selectionTempData = JSON.parse(JSON.stringify(this.multipleSelection));
+
+      }
+      // const query = this.searchQuery.toLowerCase();
+      this.$nextTick(() => {
+        // 在数据更新后,重新设置选中的项
+        if(!this.searchQuery){
+          this.setSelectionTemp();
+        } else {
+          this.availableCountriesTableForm.tempData = this.availableCountriesTableForm.availableCountriesList.filter(row => {
+            // const originalRow = this.availableCountriesTableForm.availableCountriesList.find(item => item.NAME === row.NAME);
+            // console.log(originalRow);
+            //
+            return row.NAME.toLowerCase().includes(query);
+          });
+          this.setSelection();
+
+        }
+      });
+      // this.availableCountriesTableForm.availableCountriesList.forEach(row => {
+      //   if (row.NAME.toLowerCase().includes(query)) {
+      //   } else {
+      //     const originalRow = this.filteredData.find(item => item.NAME === row.NAME);
+      //     if (originalRow) {
+      //       if(originalRow.selected === true){
+      //         console.log('originalRow true')
+      //       }
+      //       row = originalRow;
+      //
+      //     }
+      //   }
+      // });
+      // this.$refs.multipleTable.toggleRowSelection(this.selectionTempData)
+
+    },
+    setSelection() {
+      console.log(this.selectedIds)
+      console.log(this.availableCountriesTableForm.tempData)
+      this.availableCountriesTableForm.tempData.forEach(row => {
+        console.log('----1111')
+        console.log(this.selectedIds.includes(row.ID))
+        if (this.selectedIds.includes(row.ID)) {
+          console.log('----1111----------aaa')
+          console.log(row)
+          this.$nextTick(() => {
+            this.$refs.multipleTableTemp.toggleRowSelection(row, true);
+          });
+        }
+      });
+    },
+    setSelectionTemp() {
+      // this.$refs.multipleTable.clearSelection();
+      let selectedIds = JSON.parse(JSON.stringify(this.selectedIds))
+      let selectedIdsTemp = JSON.parse(JSON.stringify(this.selectedIdsTemp))
+
+      console.log('----2222')
+      let list = selectedIds.concat(selectedIdsTemp)
+      console.log("list=====+" + list)
+      this.availableCountriesTableForm.availableCountriesList.forEach(row => {
+        console.log('----3333')
+        if (list.includes(row.ID)) {
+          this.$refs.multipleTable.toggleRowSelection(row, true);
+        }
+      });
+    },
+    handleRowClick(row) {
+      console.log(row)
+
+      // 手动控制行点击时的选中状态
+      // this.$refs.multipleTable.toggleRowSelection(row,true);
+    },
+    handleRowClickTemp(row) {
+      console.log(row)
+      console.log('temp 选中')
+      // 手动控制行点击时的选中状态
+      // this.$refs.multipleTableTemp.toggleRowSelection(row,true);
+    },
+
+    handleInputBlur(row) {
+      // 当输入框失去焦点时,将修改应用到原始数据
+      let originalRow = this.availableCountriesTableForm.tempData.find(item => item.NAME === row.NAME);
+      if (originalRow) {
+        originalRow = row;
+      }
+    },
+      handleInput(value) {
+          // console.log(value)
+          // if (value < 0 || value === 0) {
+          //     this.form.sellPriceStandard = "";
+          // }
+      },
+    // 隐藏表头中的全选框
+    cellClass(row) {
+      if (row.columnIndex === 0) {
+        return 'disabledCheck'
+      }
+    },
+    isRowSelectable(row, index) {
+      return row.CURRENCY_PRODUCT_RATE == 0;
+    },
+    selectable(row, index) {
+      return row.CURRENCY_PRODUCT_RATE !== 0;
+    },
+
+    taxRateInput(scope){
+      // 检查输入值是否为正数
+      const value = scope.row.taxRate
+      if (value > 0) {
+        let moment = this.availableCountriesTableForm.availableCountriesList[scope.$index]
+        this.$set(this.availableCountriesTableForm.availableCountriesList,scope.$index, moment)
+      } else {
+        scope.row.taxRate = ''; // 更新输入框的值
+      }
+
+    },
+    marketPriceInput(scope){
+      const value = scope.row.marketPrice
+      if (value > 0) {
+        let moment = this.availableCountriesTableForm.availableCountriesList[scope.$index]
+        this.$set(this.availableCountriesTableForm.availableCountriesList,scope.$index, moment)
+      } else {
+        scope.row.marketPrice = ''; // 更新输入框的值
+
+      }
+    },
+    edit(){
+
+    },
+    submitAvailableCountries(){
+      this.$refs.availableCountriesTableForm.validate((valid) => {
+        if (valid) {
+          this.form.nature = this.multipleSelection
+          this.editAvailableCountriesDialog = false
+        } else {
+
+          return false;
+        }
+      });
+
+    },
+    handleSelectionChange(val) {
+      this.multipleSelection = val;
+      // 更新选中的项
+      this.selectedIds = val.map(item => item.ID);
+      this.updateRules();
+    },
+    handleSelectionChangeTemp(val) {
+      this.multipleSelectionTemp = val;
+      // 更新选中的项
+      this.selectedIdsTemp = val.map(item => item.ID);
+      this.updateRulesTemp();
+    },
+    updateRules() {
+      this.rules = {}; // 重置规则
+      this.multipleSelection.forEach((row, index) => {
+        this.rules[`availableCountriesList.${this.availableCountriesTableForm.availableCountriesList.indexOf(row)}.taxRate`] = [
+          { required: true, message: this.$t('shop.enterTaxRate'), trigger: 'blur' }
+        ];
+
+      });
+    },
+    updateRulesTemp() {
+      this.rules = {}; // 重置规则
+      this.multipleSelectionTemp.forEach((row, index) => {
+        this.rules[`tempData.${this.availableCountriesTableForm.tempData.indexOf(row)}.taxRate`] = [
+          { required: true, message: this.$t('shop.enterTaxRate'), trigger: 'blur' }
+        ];
+
+      });
+    },
+    selectOne(selection,row){
+      row.sellPrice = (row.CURRENCY_PRODUCT_RATE * this.form.sellPriceStandard).toFixed(2)
+      row.marketPrice = (row.sellPrice * 1.2).toFixed(2)
+    },
+    selectOneTemp(selection,row){
+      row.sellPrice = (row.CURRENCY_PRODUCT_RATE * this.form.sellPriceStandard).toFixed(2)
+      row.marketPrice = (row.sellPrice * 1.2).toFixed(2)
+    },
+    editAvailableCountries(){
+      getCountries().then(response => {
+        this.availableCountriesTableForm.availableCountriesList = response
+        this.editAvailableCountriesDialog = true
+        // this.multipleSelection = this.form.nature
+        this.toggleSelection(this.form.nature)
+      }).catch(err => {
+
+      })
+    },
+    toggleSelection(rows) {
+      if (rows) {
+        rows.forEach(item => { // checkedData为已选数据
+          this.$nextTick( ()=>{
+            this.availableCountriesTableForm.availableCountriesList.find(obj => { //  表单数据
+              if(item.ID === obj.ID) {
+                obj.sellPrice = item.sellPrice
+                obj.marketPrice = item.marketPrice
+                obj.taxRate = item.taxRate
+                this.$refs.multipleTable.toggleRowSelection(obj)
+
+              }
+            })
+          })
+        })
+      } else {
+        this.$refs.multipleTable.clearSelection();
+      }
+    },
     handleRemove (file) {
       console.log(file)
     },
@@ -191,7 +539,7 @@ export default {
                 message: this.$t('common.modifyData'),
                 type: 'info'
             })
-        }) 
+        })
     },
     getData (page, pageSize) {
       getAddGoodsConfig(this.filterModel).then(response => {
@@ -201,7 +549,7 @@ export default {
         this.sellType = response.data.sellType
       }).catch(err => {
         this.$message({
-          message: err, 
+          message: err,
           type: 'error'
         })
       })
@@ -228,8 +576,8 @@ export default {
         categoryType: this.form.categoryType,
         goodsNo: this.form.goodsNo,
         unit: this.form.unit,
-        marketPrice: this.form.marketPrice,
-        sellPrice: this.form.sellPrice,
+        // marketPrice: this.form.marketPrice,
+        // sellPrice: this.form.sellPrice,
         sellPriceStandard: this.form.sellPriceStandard,
         pricePv: this.form.pricePv,
         // point: this.form.point,
@@ -237,7 +585,8 @@ export default {
         content: this.form.content,
         sort: this.form.sort,
         cover: this.form.cover,
-        taxRate: this.form.taxRate
+        // taxRate: this.form.taxRate
+        nature: this.form.nature,
       }
       addGoods(postData).then(response => {
         this.$message({
@@ -258,13 +607,23 @@ export default {
 }
 </script>
 
-<style scoped>
+<style lang="scss" scoped>
 /* 小于760 */
  @media (max-width:759px) {
-    
+
     ::v-deep .el-input {
         width: 100% !important;
     }
+   ::v-deep .el-input-number {
+     width: 100% !important;
+     .el-input {
+       width: auto !important;
+       padding: 0 5px;
+     }
+   }
+   ::v-deep .el-input-number--medium {
+     width: 100% !important;
+   }
     ::v-deep .el-select {
         width: 100% !important;
     }
@@ -282,16 +641,26 @@ export default {
         width:100%;
         padding-left: 1%;
     }
-    ::v-deep img { 
+    ::v-deep img {
         width: 100% !important;
         height: 100% !important;
     }
-} 
+}
 
 @media (min-width:760px) {
     ::v-deep .el-input {
         width: 400px !important;
     }
+    ::v-deep .el-input-number {
+      width: 100% !important;
+      .el-input {
+        width: auto !important;
+        padding: 0 5px;
+      }
+    }
+    ::v-deep .el-input-number--medium {
+      width: 100% !important;
+    }
     ::v-deep .el-select {
         width: 400px !important;
     }
@@ -306,4 +675,31 @@ export default {
         padding-left: 10%;
     }
 }
+
+.el-input{
+  width: 90%!important;
+}
+/* 去掉全选按钮 */
+::v-deep .el-table .disabledCheck .cell .el-checkbox__inner {
+  display: none !important;
+}
+
+::v-deep .el-table .disabledCheck .cell::before {
+  content: '';
+  text-align: center;
+  line-height: 37px;
+}
+
+.left-aligned-input .el-input-number__inner {
+  text-align: left;
+}
+::v-deep .el-input-number .el-input__inner {
+  text-align: left;
+}
+
+::v-deep .el-form-item{
+  //margin-bottom: 16px !important;
+  transform: translateY(6px);
+}
+
 </style>

+ 369 - 20
src/views/shop/index.vue

@@ -104,26 +104,29 @@
             <el-form-item :label="$t('shop.unit')"><!-- 单位 -->
               <el-input v-model="form.unit"></el-input>
             </el-form-item>
-            <el-form-item :label="$t('shop.taxRate')"><!-- 税率 -->
-              <el-input v-model="form.taxRate">
-                <template slot="append">%</template>
-              </el-input>
-            </el-form-item>
-            <el-form-item :label="$t('shop.uSPrice')" p>
-              <el-input v-model="form.sellPriceStandard"></el-input>
-            </el-form-item>
-            <el-form-item :label="$t('shop.salesPrice')" p>
-              <el-input v-model="form.sellPrice"></el-input>
-            </el-form-item>
-            <el-form-item :label="$t('shop.marketPrice')">
-              <el-input v-model="form.marketPrice"></el-input>
+<!--            <el-form-item :label="$t('shop.taxRate')">&lt;!&ndash; 税率 &ndash;&gt;-->
+<!--              <el-input v-model="form.taxRate">-->
+<!--                <template slot="append">%</template>-->
+<!--              </el-input>-->
+<!--            </el-form-item>-->
+            <el-form-item :label="$t('shop.standardPrice')" p>
+              <el-input v-model="form.sellPriceStandard" @input="sellPriceStandardInput"></el-input>
             </el-form-item>
+<!--            <el-form-item :label="$t('shop.salesPrice')" p>-->
+<!--              <el-input v-model="form.sellPrice"></el-input>-->
+<!--            </el-form-item>-->
+<!--            <el-form-item :label="$t('shop.marketPrice')">-->
+<!--              <el-input v-model="form.marketPrice"></el-input>-->
+<!--            </el-form-item>-->
             <el-form-item :label="$t('shop.priceBV')" v-show="pvDisabled"> <!-- 价格BV -->
               <el-input v-model="form.pricePv"></el-input>
             </el-form-item>
   <!--                    <el-form-item label="Exchange points" v-show="false"> &lt;!&ndash; 兑换积分 &ndash;&gt;-->
   <!--                        <el-input v-model="form.point"></el-input>-->
   <!--                    </el-form-item>-->
+          <el-form-item :label="$t('shop.availableCountries')"><!-- 可购买国家 -->
+            <el-button type="primary" size="small" @click="editAvailableCountries">{{ $t('shop.modifyAvailableCountries') }}</el-button>
+          </el-form-item>
             <el-form-item :label="$t('shop.inventory')"> <!-- 库存 -->
               <el-input v-model="form.storeNums"></el-input>
             </el-form-item>
@@ -145,8 +148,8 @@
                   v-model="form.image"
                   :request-route="'v1/shop/upload'"
                   :default-image-url="form.cover"
-                  width="400px"
-                  height="160px"
+                  width="100%"
+                  height="260px"
                   @on-success="upLoadSuccess"
                 ></Upload>
               </div>
@@ -157,6 +160,99 @@
             <el-button type="primary" @click.native="handleEdit">{{ $t('table.edit') }}<!-- 修 改 --></el-button>
           </div>
       </el-dialog>
+
+      <el-dialog :title="$t('shop.modifyAvailableCountries')" :visible.sync="editAvailableCountriesDialog" top="3%" width="60%"><!-- 修改可购买国家 -->
+        <el-form ref="form"  label-width="130px" class="">
+          <el-form-item :label="$t('shop.productName')"><!-- 商品名称 -->
+            <el-input v-model="form.goodsName" style="width: 90%" disabled></el-input>
+          </el-form-item>
+          <el-form-item :label="$t('transportationConfig.countryName')">
+            <el-input
+              v-model="searchQuery"
+              placeholder=""
+              style="width: 90%"
+              @input="updateTableData"
+            ></el-input>
+          </el-form-item>
+        </el-form>
+        <el-form :model="availableCountriesTableForm" ref="availableCountriesTableForm" :rules="rules">
+          <el-table v-show="!searchQuery" :data="availableCountriesTableForm.availableCountriesList" ref="multipleTableCountries" @selection-change="countriesSelectionChange" @select="selectOne" :selectable="isRowSelectable" stripe :header-cell-class-name="cellClass" height="500" style="width: 100%;">
+            <el-table-column
+                type="selection"
+                width="55"
+                :selectable="selectable"
+                :show-select-all="false"
+            >
+            </el-table-column>
+            <el-table-column prop="NAME" :label="$t('transportationConfig.countryName')" />
+            <el-table-column prop="LOCAL_CURRENCY_NAME" :label="$t('exchangeRateConfig.currencyType')" />
+            <el-table-column :label="$t('shop.salesPrice')" min-width="100px;">
+              <template slot-scope="scope">
+                <el-form-item :prop="`availableCountriesList.${scope.$index}.sellPrice`" >
+                  <el-input-number v-model="scope.row.sellPrice" :controls="false"></el-input-number>
+                </el-form-item>
+              </template>
+            </el-table-column>
+            <el-table-column :label="$t('shop.marketPrice')" min-width="100px;">
+              <template slot-scope="scope">
+                <el-form-item :prop="`availableCountriesList.${scope.$index}.marketPrice`" >
+                  <el-input-number v-model="scope.row.marketPrice" :precision="2" :min="0.01" :controls="false" clearable></el-input-number>
+                </el-form-item>
+              </template>
+            </el-table-column>
+            <el-table-column :label="$t('shop.taxRate')" min-width="100px;">
+              <template slot-scope="scope">
+                <el-form-item :prop="`availableCountriesList.${scope.$index}.taxRate`" >
+                  <div class="flexJfAc">
+                    <el-input-number v-model="scope.row.taxRate" :precision="2" :min="0.01" :controls="false"></el-input-number><span>%</span>
+                  </div>
+                </el-form-item>
+              </template>
+            </el-table-column>
+          </el-table>
+
+            <el-table v-show="searchQuery" :data="availableCountriesTableForm.tempData" ref="multipleTableTemp" @row-click="handleRowClickTemp" @selection-change="handleSelectionChangeTemp" @select="selectOneTemp" :selectable="isRowSelectable" :header-cell-class-name="cellClass" stripe height="500" style="width: 100%;">
+                <el-table-column
+                        type="selection"
+                        width="55"
+                        :selectable="selectable"
+                        :show-select-all="false"
+                >
+                </el-table-column>
+                <el-table-column prop="NAME" :label="$t('transportationConfig.countryName')" />
+                <el-table-column prop="LOCAL_CURRENCY_NAME" :label="$t('exchangeRateConfig.currencyType')" />
+                <el-table-column :label="$t('shop.salesPrice')" min-width="100px;">
+                    <template slot-scope="scope">
+                        <el-form-item :prop="`tempData.${scope.$index}.sellPrice`" >
+                            <el-input-number v-model="scope.row.sellPrice" :controls="false" disabled></el-input-number>
+                        </el-form-item>
+                    </template>
+                </el-table-column>
+                <el-table-column :label="$t('shop.marketPrice')" min-width="100px;">
+                    <template slot-scope="scope">
+                        <el-form-item :prop="`tempData.${scope.$index}.marketPrice`">
+                            <el-input-number v-model="scope.row.marketPrice" :precision="2" :min="0" :controls="false" clearable></el-input-number>
+                        </el-form-item>
+                    </template>
+                </el-table-column>
+                <el-table-column :label="$t('shop.taxRate')" min-width="100px;">
+                    <template slot-scope="scope">
+                        <el-form-item :prop="`tempData.${scope.$index}.taxRate`" >
+                            <div class="flexJfAc">
+                                <el-input-number v-model="scope.row.taxRate" :precision="2" :step="0.1" :min="0" :controls="false"></el-input-number><span>%</span>
+                            </div>
+                        </el-form-item>
+                    </template>
+                </el-table-column>
+            </el-table>
+
+
+        </el-form>
+        <div slot="footer" class="dialog-footer">
+          <el-button type="warning" size="mini" @click="editAvailableCountriesDialog = false">{{ $t('table.cancel') }}</el-button>
+          <el-button type="primary" size="mini" @click="submitAvailableCountries">{{ $t('table.confirm') }}</el-button>
+        </div>
+      </el-dialog>
     </div>
   </template>
 
@@ -251,9 +347,191 @@
         pvDisabled: true,
         exchangeRate: baseInfo.exchangeRate(),
         labelPosition: getScreenWidth() >= 600 ? 'left' : 'top',
+        editAvailableCountriesDialog: false,
+        availableCountriesTableForm: {
+          availableCountriesList: [],
+          tempData:[],
+        },
+        multipleSelectionCountries: [],
+          multipleSelectionTemp: [],
+
+        rules: {},
+          rulesTemp: {},
+          searchQuery: '',
+
+          selectionTempData: [],
+          selectedIdsCountry: [], // 存储选中的项的 ID
+          selectedIdsTemp: [],
       }
     },
     methods: {
+        updateTableData() {
+            let that = this;
+            // if (!this.searchQuery) {
+            //   return this.availableCountriesTableForm.availableCountriesList;
+            // }
+            // 临时存储原始数据,保持搜索时不改变其他列的值
+            const query = that.searchQuery.toLowerCase();
+            // this.$refs.multipleTable.toggleRowSelection(JSON.parse(JSON.stringify(this.multipleSelection)),true)
+
+            if (that.availableCountriesTableForm.tempData.length === 0) {
+                // this.tempData = JSON.parse(JSON.stringify(this.availableCountriesTableForm.availableCountriesList));
+                // this.selectionTempData = JSON.parse(JSON.stringify(this.multipleSelection));
+
+            }
+            // const query = this.searchQuery.toLowerCase();
+            that.$nextTick(() => {
+                // 在数据更新后,重新设置选中的项
+                console.log(this.searchQuery === "")
+                console.log(query)
+
+                if(!that.searchQuery){
+                    console.log('no---')
+                    that.setSelectionTemp();
+                } else {
+                    console.log('yes---')
+                    that.availableCountriesTableForm.tempData = that.availableCountriesTableForm.availableCountriesList.filter(row => {
+                        return row.NAME.toLowerCase().includes(query);
+                    });
+                    that.setSelection();
+
+                }
+            });
+            // this.availableCountriesTableForm.availableCountriesList.forEach(row => {
+            //   if (row.NAME.toLowerCase().includes(query)) {
+            //   } else {
+            //     const originalRow = this.filteredData.find(item => item.NAME === row.NAME);
+            //     if (originalRow) {
+            //       if(originalRow.selected === true){
+            //         console.log('originalRow true')
+            //       }
+            //       row = originalRow;
+            //
+            //     }
+            //   }
+            // });
+            // this.$refs.multipleTable.toggleRowSelection(this.selectionTempData)
+
+        },
+        setSelection() {
+            // this.$refs.multipleTable.clearSelection();
+            console.log('----00000')
+            this.availableCountriesTableForm.tempData.forEach(row => {
+                console.log('----1111')
+                if (this.selectedIdsCountry.includes(row.ID)) {
+                  this.$nextTick(() => {
+                    this.$refs.multipleTableTemp.toggleRowSelection(row, true);
+                  });
+                }
+            });
+        },
+        setSelectionTemp() {
+            // this.$refs.multipleTable.clearSelection();
+            let selectedIds = JSON.parse(JSON.stringify(this.selectedIdsCountry))
+            let selectedIdsTemp = JSON.parse(JSON.stringify(this.selectedIdsTemp))
+
+            console.log('----2222')
+            let list = selectedIds.concat(selectedIdsTemp)
+            console.log("list=====+" + list)
+            this.availableCountriesTableForm.availableCountriesList.forEach(row => {
+                console.log('----3333')
+                if (list.includes(row.ID)) {
+                    this.$refs.multipleTableCountries.toggleRowSelection(row, true);
+                }
+            });
+        },
+        handleRowClickTemp(row) {
+            // 手动控制行点击时的选中状态
+            // this.$refs.multipleTableTemp.toggleRowSelection(row,true);
+        },
+        handleSelectionChangeTemp(val) {
+            this.multipleSelectionTemp = val;
+            // 更新选中的项
+            this.selectedIdsTemp = val.map(item => item.ID);
+            this.updateRulesTemp();
+        },
+        updateRulesTemp() {
+            this.rules = {}; // 重置规则
+            this.multipleSelectionTemp.forEach((row, index) => {
+                this.rules[`tempData.${this.availableCountriesTableForm.tempData.indexOf(row)}.taxRate`] = [
+                    { required: true, message: this.$t('shop.enterTaxRate'), trigger: 'blur' }
+                ];
+
+            });
+        },
+
+        // 隐藏表头中的全选框
+      cellClass(row) {
+        if (row.columnIndex === 0) {
+          return 'disabledCheck'
+        }
+      },
+      isRowSelectable(row, index) {
+        return row.CURRENCY_PRODUCT_RATE == 0;
+      },
+      selectable(row, index) {
+        return row.CURRENCY_PRODUCT_RATE !== 0;
+      },
+      sellPriceStandardInput(value){
+        console.log(value)
+        // this.form.nature = []
+        this.toggleSelection(this.form.nature, 'input')
+
+      },
+      countriesSelectionChange(val) {
+        this.multipleSelectionCountries = val;
+          this.selectedIdsCountry = val.map(item => item.ID);
+
+        this.updateRules();
+      },
+      updateRules() {
+        this.rules = {}; // 重置规则
+        this.multipleSelectionCountries.forEach((row, index) => {
+          this.rules[`availableCountriesList.${this.availableCountriesTableForm.availableCountriesList.indexOf(row)}.taxRate`] = [
+            { required: true, message: this.$t('shop.enterTaxRate'), trigger: 'blur' }
+          ];
+
+        });
+      },
+      selectOne(selection,row){
+        row.sellPrice = (row.CURRENCY_PRODUCT_RATE * this.form.sellPriceStandard).toFixed(2)
+        row.marketPrice = (row.sellPrice * 1.2).toFixed(2)
+      },
+        selectOneTemp(selection,row){
+            console.log(row.CURRENCY_PRODUCT_RATE)
+            // if(!row.CURRENCY_PRODUCT_RATE){
+            //   return false
+            // }
+            row.sellPrice = (row.CURRENCY_PRODUCT_RATE * this.form.sellPriceStandard).toFixed(2)
+            row.marketPrice = (row.sellPrice * 1.2).toFixed(2)
+        },
+      taxRateInput(scope){
+        const value = parseFloat(scope.row.taxRate) || 0;
+        if (value <= 0) {
+          scope.row.taxRate = ''; // 更新输入框的值
+        } else {
+          let moment = this.availableCountriesTableForm.availableCountriesList[scope.$index]
+          this.$set(this.availableCountriesTableForm.availableCountriesList,scope.$index, moment)
+        }
+      },
+      marketPriceInput(scope){
+        const value = parseFloat(scope.row.marketPrice) || 0;
+        if (value <= 0) {
+          scope.row.marketPrice = ''; // 更新输入框的值
+        } else {
+          let moment = this.availableCountriesTableForm.availableCountriesList[scope.$index]
+          this.$set(this.availableCountriesTableForm.availableCountriesList,scope.$index, moment)
+        }
+      },
+      submitAvailableCountries(){
+        this.form.nature = this.multipleSelectionCountries
+        this.editAvailableCountriesDialog = false
+      },
+      editAvailableCountries(){
+        this.editAvailableCountriesDialog = true
+        this.toggleSelection(this.form.nature,'data')
+
+      },
       handleSelectionChange (val) {
         this.multipleSelection = val
       },
@@ -323,12 +601,60 @@
             vueObj.form.taxRate = response.data.goodsInfo.TAX_RATE
             vueObj.form.coverOrigin = response.data.goodsInfo.COVER
             vueObj.form.cover = tool.getArImage(response.data.goodsInfo.COVER, '/files/')
+            vueObj.availableCountriesTableForm.availableCountriesList = response.data.goodsInfo.SHOP_GOODS_NATURE
+            const goods = JSON.parse(JSON.stringify(response.data.goodsInfo.SHOP_GOODS_NATURE))
+            let nature = []
+            goods.find(item => { //  表单数据
+              if(item.SHOP_GOODS_NATURE.length > 0) {
+                item.sellPrice = item.SHOP_GOODS_NATURE[0].SELL_PRICE
+                item.taxRate = item.SHOP_GOODS_NATURE[0].TAX_RATE
+                item.marketPrice = item.SHOP_GOODS_NATURE[0].MARKET_PRICE
+                nature.push(item)
+              }
+              vueObj.form.nature = JSON.parse(JSON.stringify(nature))
+            })
+            console.log(vueObj.form.nature)
 
             this.$forceUpdate()
         }).catch(err => {
 
         })
       },
+      toggleSelection(rows, type) {
+        let data = JSON.parse(JSON.stringify(rows))
+        if(data.length > 0 && type === 'input'){
+          let checkedData = this.form.nature
+          checkedData.forEach(item => { // checkedData为已选数据
+            this.$nextTick( ()=>{
+              this.availableCountriesTableForm.availableCountriesList.find(obj => { //  表单数据
+                if(item.ID === obj.ID) {
+                  obj.sellPrice = (item.CURRENCY_PRODUCT_RATE * this.form.sellPriceStandard).toFixed(2)
+                  obj.taxRate = item.taxRate
+                  obj.marketPrice = (obj.sellPrice * 1.2).toFixed(2)
+                  this.$refs.multipleTableCountries.toggleRowSelection(obj,true)
+                }
+              })
+            })
+          })
+          return false
+        }
+        else if (data.length > 0 && type === 'data') {
+          data.forEach(item => { // checkedData为已选数据
+            this.$nextTick( ()=>{
+              this.availableCountriesTableForm.availableCountriesList.find(obj => { //  表单数据
+                if(item.ID === obj.ID) {
+                  obj.sellPrice = item.sellPrice
+                  obj.taxRate = item.taxRate
+                  obj.marketPrice = item.marketPrice
+                  this.$refs.multipleTableCountries.toggleRowSelection(obj,true)
+                }
+              })
+            })
+          })
+        } else {
+          this.$refs.multipleTableCountries.clearSelection();
+        }
+      },
       handleEdit () {
         this.dialogEditFormVisible = false
         this.$message({
@@ -468,7 +794,7 @@
                 })
             })
         })
-      }
+      },
     },
     watch: {
       // 监听商品分类,控制PV是否展示
@@ -477,12 +803,12 @@
         handler (modern, origin) {
           this.pvDisabled = (parseInt(modern) === 1)
         }
-      }
-      // // 监听商品标准价格,自动计算销售价格
+      },
+      // 监听商品标准价格,自动计算销售价格
       // 'form.sellPriceStandard': {
       //     deep: true,
       //     handler(modern, origin) {
-      //         this.form.sellPrice = modern * this.exchangeRate
+      //
       //     }
       // },
     }
@@ -509,11 +835,34 @@
         width: 100% !important;
         height: 100% !important;
       }
-      
+
       @media (max-width:862px) {
         /deep/ .el-dialog {
           width:100%;
         }
       }
+      ::v-deep .el-input-number {
+        width: 100% !important;
+        .el-input {
+          width: auto !important;
+          padding: 0 5px;
+        }
+      }
+
+      ::v-deep .el-input-number .el-input__inner {
+        text-align: left;
+      }
+      ::v-deep .el-form-item{
+        transform: translateY(6px);
+      }
+      /* 去掉全选按钮 */
+      ::v-deep .el-table .disabledCheck .cell .el-checkbox__inner {
+        display: none !important;
+      }
 
+      ::v-deep .el-table .disabledCheck .cell::before {
+        content: '';
+        text-align: center;
+        line-height: 37px;
+      }
   </style>

+ 1 - 1
src/views/user/change-highest-emp-level.vue

@@ -91,7 +91,7 @@
       <el-form
         ref="form"
         :model="form"
-        label-width="170px"
+        label-width="180px"
         :label-position="labelPosition"
       >
         <el-form-item :label="$t('member.memberCode')" required>

+ 38 - 4
src/views/user/empty-order-operation.vue

@@ -6,7 +6,16 @@
           <el-form ref="form" :model="form" label-width="250px" :label-position="labelPosition">
 
             <div class="hr-tip"><span>{{ $t('member.accountInformation') }}</span></div>
-
+            <el-form-item :label="$t('transportationConfig.countryName')" required>
+              <el-select ref="select" @change="countriesChange" v-model="form.countryId" filterable placeholder="">
+                <el-option v-for="(item, index) in countries" :key="item.ID" :label="item.NAME" :value="item.ID" />
+              </el-select>
+            </el-form-item>
+            <el-form-item :label="$t('common.language')" required>
+              <el-select ref="select" v-model="form.languageId" filterable placeholder="">
+                <el-option v-for="(item, index) in languageList" :key="item.ID" :label="item.NAME" :value="item.ID" />
+              </el-select>
+            </el-form-item>
             <el-form-item :label="$t('member.memberCode')" required>
               <el-input v-model="form.userName"></el-input>
             </el-form-item>
@@ -96,15 +105,18 @@
 	import {getScreenWidth} from "@/utils"
 	import region from "@/store/modules/region"
 	import {fetchMemberFullInfo, fetchUserAdd, updateUserAdd} from "@/api/member"
-
+  import { getCountries, getLanguages } from "@/api/site"
   export default {
     name: 'emptyOrderOperation',
     mounted() {
-      this.getData()
+      this.getCountries()
+      this.getLanguageList()
     },
     data() {
       return {
         form: {
+          countryId: '',
+          languageId: '',
           userName: '',
           nation: '',
           realName: '',
@@ -160,9 +172,31 @@
         permission: permission,
 				screenWidth: getScreenWidth() > 650 ? '650px' : getScreenWidth() + 'px',
 				labelPosition: getScreenWidth() >= 600 ? 'right' : 'top',
+        countries: [],
+        languageList: []
       }
     },
     methods: {
+      countriesChange(e){
+        this.getData()
+      },
+      getLanguageList(){
+        getLanguages().then(response => {
+          let obj = response
+          let list = []
+          Object.keys(obj).forEach(function(key) {
+            list.push(obj[key])
+          });
+          this.languageList = list
+        })
+      },
+      getCountries(){
+        getCountries().then(response => {
+          this.countries = response
+        }).catch(err => {
+
+        })
+      },
       getData() {
         if (permission.hasPermission(`user/user-del`)) {
           this.tabActiveName = 'third'
@@ -173,7 +207,7 @@
         if (permission.hasPermission(`user/user-add`)) {
           this.tabActiveName = 'first'
 					this.loading = true
-					fetchUserAdd().then(response => {
+					fetchUserAdd({countryId: this.form.countryId}).then(response => {
 						this.loading = false
 						this.allNation = response.data.allNation
 						this.allOpenBank = response.data.allOpenBank

+ 58 - 3
src/views/user/member-list.vue

@@ -112,7 +112,7 @@
 		</el-dialog>
 
 		<!-- 修改会员信息 -->
-		<el-dialog :title="formModifyProfile.typeName" :visible.sync="dialogModifyProfileVisible" :width="screenWidth">
+		<el-dialog :title="formModifyProfile.typeName" :visible.sync="dialogModifyProfileVisible" :width="screenWidth" :destroy-on-close="true">
 			<el-form ref="form" :model="formModifyProfile" label-width="150px" :label-position="labelPosition">
 				<el-form-item :label="$t('member.memberName')">
 					<el-input v-model="formModifyProfile.realName"></el-input>
@@ -120,6 +120,16 @@
 				<el-form-item :label="$t('member.identityNo')">
 					<el-input v-model="formModifyProfile.idCard"></el-input>
 				</el-form-item>
+        <el-form-item :label="$t('transportationConfig.countryName')">
+          <el-select v-model="formModifyProfile.country" filterable ref="countryId" @change="countryChange" @blur="countryVisibleChange($event)">
+            <el-option v-for="(item,index) in allCountry" :key="index" :label="item.NAME" :value="item.ID"></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item :label="$t('common.language')">
+          <el-select v-model="formModifyProfile.language">
+            <el-option v-for="(item,index) in allLanguage" :key="index" :label="item.NAME" :value="item.ID"></el-option>
+          </el-select>
+        </el-form-item>
 				<el-form-item :label="$t('member.phoneNumber')">
 					<el-input v-model="formModifyProfile.mobile"></el-input>
 				</el-form-item>
@@ -156,6 +166,7 @@ import {
 	fetchCloseDec, fetchCloseDecGet, fetchCloseLoginGet, fetchMemberExport, fetchMemberList, fetchProfileGet,
 	updateModifyPassword, updateModifyProfile, updateSetActive, updateSetAsStockist, updateSetChartDisplay, updateSetLock, updateSetModifyPassword, updateSetRechargeDisplay,
 } from "@/api/member"
+import {getBank} from "@/api/site";
 
 export default {
 	name: 'memberList',
@@ -274,6 +285,8 @@ export default {
 			],
 			allOpenBank:null,
 			allNation:null,
+      allCountry: null,
+      allLanguage: null,
 			submitDecButtonStat: false,
 			submitPasswordButtonStat: false,
 			submitProfileButtonStat: false,
@@ -291,9 +304,48 @@ export default {
 			fixed: getScreenWidth() < 500 ? false : 'right',
 			// leftFixed: ['Android', 'ios'].includes(getOperatingSystem()) ? false : 'left',
 			// fixed: ['Android', 'ios'].includes(getOperatingSystem()) ? false : 'right',
+      originalCountry: '',
 		}
 	},
 	methods: {
+    countryVisibleChange(event){
+    },
+    countryChange(row){
+      this.$nextTick(() => {
+        this.$refs.countryId.blur()
+        this.$confirm( this.$t('member.modifyCountry'), this.$t('common.hint'), {
+          confirmButtonText: this.$t('common.confirm'),
+          cancelButtonText: this.$t('common.cancel'),
+          type: 'warning'
+        }).then(() => {
+          // this.$message({
+          //   type: 'success',
+          //   message: '!'
+          // });
+          this.getBanks(row)
+
+        }).catch(() => {
+          this.formModifyProfile.country = this.originalCountry
+          this.getBanks(this.originalCountry)
+        });
+
+      });
+
+
+    },
+
+    getBanks(countryId){
+      getBank( { countryId: countryId, }).then(response => {
+        this.formModifyProfile.openBank = null
+        this.allOpenBank = response
+      }).catch(error => {
+        this.$message({
+          message: error,
+          type: 'warning'
+        })
+
+      })
+    },
 		handleSelectionChange(val) {
 			this.multipleSelection = val
 		},
@@ -472,6 +524,9 @@ export default {
 				this.formModifyProfile = response.data.userInfo
 				this.allOpenBank = response.data.allOpenBank
 				this.allNation = response.data.allNation
+        this.allCountry = response.data.country
+        this.allLanguage = response.data.language
+        this.originalCountry = JSON.parse(JSON.stringify(response.data.userInfo.country))
 			}).catch(error => {
 				this.$message({
 					message: error,
@@ -566,10 +621,10 @@ export default {
 			}).catch(error => {
 				this.$message({
 					message: error,
-					type: 'success'
+					type: 'warning'
 				})
 				this.submitProfileButtonStat = false
-				this.dialogModifyProfileVisible = false
+				// this.dialogModifyProfileVisible = false
 			})
 		},
 	}