Эх сурвалжийг харах

feat: add area cascade selector demo (#257)

一万 3 жил өмнө
parent
commit
016c75c0d4

+ 2 - 1
locales/en.yaml

@@ -79,4 +79,5 @@ menus:
   hsDebounce: Debounce & Throttle
   hsFormDesign: Form Design
   hsBarcode: Barcode
-  hsQarcode: Qarcode
+  hsQrcode: Qrcode
+  hsCascader: Cascader

+ 2 - 1
locales/zh-CN.yaml

@@ -79,4 +79,5 @@ menus:
   hsDebounce: 防抖节流
   hsFormDesign: 表单设计器
   hsBarcode: 条形码
-  hsQarcode: 二维码
+  hsQrcode: 二维码
+  hsCascader: 级联选择器

+ 2 - 1
package.json

@@ -39,12 +39,13 @@
     "@wangeditor/editor-for-vue": "^5.1.10",
     "animate.css": "^4.1.1",
     "axios": "^0.26.1",
+    "china-area-data": "^5.0.1",
     "cropperjs": "^1.5.12",
     "css-color-function": "^1.3.3",
     "dayjs": "^1.11.0",
     "driver.js": "^0.9.8",
     "echarts": "^5.3.2",
-    "element-plus": "^2.1.10",
+    "element-plus": "^2.1.11",
     "element-resize-detector": "^1.2.3",
     "js-cookie": "^3.0.1",
     "jsbarcode": "^3.11.5",

+ 39 - 23
pnpm-lock.yaml

@@ -43,13 +43,14 @@ specifiers:
   animate.css: ^4.1.1
   autoprefixer: ^10.4.5
   axios: ^0.26.1
+  china-area-data: ^5.0.1
   cropperjs: ^1.5.12
   cross-env: 7.0.3
   css-color-function: ^1.3.3
   dayjs: ^1.11.0
   driver.js: ^0.9.8
   echarts: ^5.3.2
-  element-plus: ^2.1.10
+  element-plus: ^2.1.11
   element-resize-detector: ^1.2.3
   eslint: ^8.8.0
   eslint-plugin-prettier: ^4.0.0
@@ -122,12 +123,13 @@ dependencies:
   "@wangeditor/editor-for-vue": 5.1.10_9016b5918024e821ee2af40b62ae7476
   animate.css: 4.1.1
   axios: 0.26.1
+  china-area-data: 5.0.1
   cropperjs: 1.5.12
   css-color-function: 1.3.3
   dayjs: 1.11.0
   driver.js: 0.9.8
   echarts: 5.3.2
-  element-plus: 2.1.10_vue@3.2.33
+  element-plus: 2.1.11_vue@3.2.33
   element-resize-detector: 1.2.4
   js-cookie: 3.0.1
   jsbarcode: 3.11.5
@@ -942,20 +944,20 @@ packages:
       - supports-color
     dev: true
 
-  /@floating-ui/core/0.6.1:
+  /@floating-ui/core/0.6.2:
     resolution:
       {
-        integrity: sha512-Y30eVMcZva8o84c0HcXAtDO4BEzPJMvF6+B7x7urL2xbAqVsGJhojOyHLaoQHQYjb6OkqRq5kO+zeySycQwKqg==
+        integrity: sha512-jktYRmZwmau63adUG3GKOAVCofBXkk55S/zQ94XOorAHhwqFIOFAy1rSp2N0Wp6/tGbe9V3u/ExlGZypyY17rg==
       }
     dev: false
 
-  /@floating-ui/dom/0.4.4:
+  /@floating-ui/dom/0.4.5:
     resolution:
       {
-        integrity: sha512-0Ulu3B/dqQplUUSqnTx0foSrlYuMN+GTtlJWvNJwt6Fr7/PqmlR/Y08o6/+bxDWr6p3roBJRaQ51MDZsNmEhhw==
+        integrity: sha512-b+prvQgJt8pieaKYMSJBXHxX/DYwdLsAWxKYqnO5dO2V4oo/TYBZJAUQCVNjTWWsrs6o4VDrNcP9+E70HAhJdw==
       }
     dependencies:
-      "@floating-ui/core": 0.6.1
+      "@floating-ui/core": 0.6.2
     dev: false
 
   /@humanwhocodes/config-array/0.9.5:
@@ -1278,13 +1280,6 @@ packages:
       fastq: 1.13.0
     dev: true
 
-  /@popperjs/core/2.11.5:
-    resolution:
-      {
-        integrity: sha512-9X2obfABZuDVLCgPK9aX0a/x4jaOEweTTWE2+9sr0Qqqevj2Uv5XorvusThmc9XGYpS9yI+fhh8RTafBtGposw==
-      }
-    dev: false
-
   /@pureadmin/components/1.0.6_vue@3.2.33:
     resolution:
       {
@@ -1363,6 +1358,13 @@ packages:
       nanopop: 2.1.0
     dev: false
 
+  /@sxzz/popperjs-es/2.11.7:
+    resolution:
+      {
+        integrity: sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==
+      }
+    dev: false
+
   /@transloadit/prettier-bytes/0.0.7:
     resolution:
       {
@@ -1427,10 +1429,10 @@ packages:
         integrity: sha512-XOKXa1KIxtNXgASAnwj7cnttJxS4fksBRywK/9LzRV5YxrF80BXZIGeQSuoESQ/VkUj30Ae0+YcuHc15wJCB2g==
       }
 
-  /@types/lodash/4.14.181:
+  /@types/lodash/4.14.182:
     resolution:
       {
-        integrity: sha512-n3tyKthHJbkiWhDZs3DkhkCzt2MexYHXlX0td5iMplyfwketaOeKboEVBqzceH7juqvEg3q5oUoBFxSLu7zFag==
+        integrity: sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q==
       }
     dev: false
 
@@ -2835,6 +2837,13 @@ packages:
       supports-color: 7.2.0
     dev: true
 
+  /china-area-data/5.0.1:
+    resolution:
+      {
+        integrity: sha512-BQDPpiv5Nn+018ekcJK2oSD9PAD+E1bvXB0wgabc//dFVS/KvRqCgg0QOEUt3vBkx9XzB5a9BmkJCEZDBxVjVw==
+      }
+    dev: false
+
   /chokidar/3.5.3:
     resolution:
       {
@@ -3370,6 +3379,13 @@ packages:
       }
     dev: false
 
+  /dayjs/1.11.1:
+    resolution:
+      {
+        integrity: sha512-ER7EjqVAMkRRsxNCC5YqJ9d9VQYuWdGt7aiH2qA5R5wt8ZmWaP2dLUSIK6y/kVzLMlmh1Tvu5xUf4M/wdGJ5KA==
+      }
+    dev: false
+
   /debug/2.6.9:
     resolution:
       {
@@ -3587,23 +3603,23 @@ packages:
       }
     dev: true
 
-  /element-plus/2.1.10_vue@3.2.33:
+  /element-plus/2.1.11_vue@3.2.33:
     resolution:
       {
-        integrity: sha512-sS9OMgP20dlYipmzHlEEgCJU+ID7+03YpRpoJWNQEH736C6ArmDMLnGFe8DUjPvwbUEXRA2d0Eo5d0apFgkSqg==
+        integrity: sha512-s4X0I8s787tv+9UdekBC1g7v42Fj4bucPAmu03EjbgrGrV7BJvkoBGuK52lNfu4yC76bl6Uyjesd5Fu8CMakSw==
       }
     peerDependencies:
       vue: ^3.2.0
     dependencies:
       "@ctrl/tinycolor": 3.4.1
       "@element-plus/icons-vue": 1.1.4_vue@3.2.33
-      "@floating-ui/dom": 0.4.4
-      "@popperjs/core": 2.11.5
-      "@types/lodash": 4.14.181
+      "@floating-ui/dom": 0.4.5
+      "@popperjs/core": /@sxzz/popperjs-es/2.11.7
+      "@types/lodash": 4.14.182
       "@types/lodash-es": 4.17.6
       "@vueuse/core": 8.3.1_vue@3.2.33
       async-validator: 4.0.7
-      dayjs: 1.11.0
+      dayjs: 1.11.1
       escape-html: 1.0.3
       lodash: 4.17.21
       lodash-es: 4.17.21
@@ -8039,7 +8055,7 @@ packages:
       ace-builds: 1.4.14
       ant-design-vue: 3.2.0_vue@3.2.33
       core-js: 3.22.0
-      element-plus: 2.1.10_vue@3.2.33
+      element-plus: 2.1.11_vue@3.2.33
       lodash: 4.17.21
       uuid: 8.3.2
       vue: 3.2.33

+ 1 - 1
src/components/ReQrcode/src/index.tsx

@@ -53,7 +53,7 @@ const props = {
 };
 
 export default defineComponent({
-  name: "epTableProBar",
+  name: "ReQrcode",
   props,
   emits: ["done", "click", "disabled-click"],
   setup(props, { emit }) {

+ 11 - 2
src/router/modules/able.ts

@@ -113,10 +113,19 @@ const ableRouter = {
     },
     {
       path: "/able/qrcode",
-      name: "reQarcode",
+      name: "reQrcode",
       component: () => import("/@/views/able/qrcode.vue"),
       meta: {
-        title: $t("menus.hsQarcode"),
+        title: $t("menus.hsQrcode"),
+        i18n: true
+      }
+    },
+    {
+      path: "/able/cascader",
+      name: "reCascader",
+      component: () => import("/@/views/able/cascader.vue"),
+      meta: {
+        title: $t("menus.hsCascader"),
         i18n: true
       }
     }

+ 5 - 5
src/style/sidebar.scss

@@ -30,6 +30,11 @@
     margin-left: $sideBarWidth;
     position: relative;
     background: #f0f2f5;
+
+    .el-scrollbar__wrap {
+      overflow: auto;
+      height: 100%;
+    }
   }
 
   .fixed-header {
@@ -483,11 +488,6 @@
     }
   }
 
-  .el-scrollbar__wrap {
-    overflow: auto;
-    height: 100%;
-  }
-
   .el-menu--collapse .el-menu .el-sub-menu {
     min-width: $sideBarWidth !important;
   }

+ 190 - 0
src/utils/chinaArea.ts

@@ -0,0 +1,190 @@
+import REGION_DATA from "china-area-data";
+import { cloneDeep } from "lodash-unified";
+
+interface ProvinceData {
+  value: string;
+  label: string;
+  children?: Array<ProvinceData>;
+}
+
+// code转汉字大对象,例:CodeToText['110000']输出北京市
+const CodeToText = {};
+// 汉字转code大对象,例:TextToCode['北京市']['市辖区']['朝阳区'].code输出110105
+const TextToCode = {};
+// 省份对象
+const provinceObject = REGION_DATA["86"];
+// 省市区三级联动数据(不带“全部”选项)
+const regionData = [];
+// 省市二级联动数据(不带“全部”选项)
+let provinceAndCityData = [];
+
+const ALL_TEXT = "全部";
+
+CodeToText[""] = ALL_TEXT;
+
+// 计算省
+Object.keys(provinceObject).forEach(prop => {
+  const provinceText = provinceObject[prop];
+  regionData.push({
+    value: prop, // 省份code值
+    label: provinceText // 省份汉字
+  });
+  CodeToText[prop] = provinceText;
+  TextToCode[provinceText] = {
+    code: prop
+  };
+  TextToCode[provinceText][ALL_TEXT] = {
+    code: ""
+  };
+});
+
+// 计算市
+regionData.forEach((item: ProvinceData) => {
+  const provinceCode = item.value;
+  const provinceText = item.label;
+  const provinceChildren = [];
+  const provinceData = REGION_DATA[provinceCode] ?? {};
+
+  Object.keys(provinceData).forEach(prop => {
+    provinceChildren.push({
+      value: prop,
+      label: provinceData[prop]
+    });
+    CodeToText[prop] = provinceData[prop];
+    TextToCode[provinceText][provinceData[prop]] = {
+      code: prop
+    };
+    TextToCode[provinceText][provinceData[prop]][ALL_TEXT] = {
+      code: ""
+    };
+  });
+
+  if (provinceChildren.length) {
+    item.children = provinceChildren;
+  }
+});
+provinceAndCityData = cloneDeep(regionData);
+
+// 计算区
+regionData.forEach((item: ProvinceData) => {
+  const province = item.children;
+  const provinceText = item.label;
+
+  if (province) {
+    province.forEach(pItem => {
+      const cityCode = pItem.value;
+      const cityText = pItem.label;
+      const cityChildren = [];
+      const cityData = REGION_DATA[cityCode] ?? {};
+
+      Object.keys(cityData).forEach(prop => {
+        cityChildren.push({
+          value: prop,
+          label: cityData[prop]
+        });
+        CodeToText[prop] = cityData[prop];
+        TextToCode[provinceText][cityText][cityData[prop]] = {
+          code: prop
+        };
+      });
+
+      if (cityChildren.length) {
+        pItem.children = cityChildren;
+      }
+    });
+  }
+});
+
+// 添加“全部”选项
+const provinceAndCityDataPlus = cloneDeep(provinceAndCityData);
+provinceAndCityDataPlus.unshift({
+  value: "",
+  label: ALL_TEXT
+});
+provinceAndCityDataPlus.forEach((item: ProvinceData) => {
+  const province = item.children;
+
+  if (province?.length) {
+    province.unshift({
+      value: "",
+      label: ALL_TEXT
+    });
+
+    province.forEach(pItem => {
+      const city = pItem.children;
+
+      if (city?.length) {
+        city.unshift({
+          value: "",
+          label: ALL_TEXT
+        });
+      }
+    });
+  }
+});
+
+const regionDataPlus = cloneDeep(regionData);
+regionDataPlus.unshift({
+  value: "",
+  label: ALL_TEXT
+});
+regionDataPlus.forEach((item: ProvinceData) => {
+  const province = item.children;
+
+  if (province?.length) {
+    province.unshift({
+      value: "",
+      label: ALL_TEXT
+    });
+    province.forEach(pItem => {
+      const city = pItem.children;
+
+      if (city?.length) {
+        city.unshift({
+          value: "",
+          label: ALL_TEXT
+        });
+      }
+    });
+  }
+});
+
+/**
+ * 汉字转区域码
+ * @param provinceText 省
+ * @param cityText 市
+ * @param regionText 区
+ * @returns
+ */
+function convertTextToCode(
+  provinceText: string,
+  cityText: string,
+  regionText?: string
+): string {
+  let code = "";
+  if (provinceText && TextToCode[provinceText]) {
+    const province = TextToCode[provinceText];
+    code = province.code;
+
+    if (cityText && province[cityText]) {
+      const city = province[cityText];
+      code = `${code}${cityText === ALL_TEXT ? "" : ", "}${city.code}`;
+
+      if (regionText && city[regionText]) {
+        code = `${code}${regionText === ALL_TEXT ? "" : ", "}${
+          city[regionText].code
+        }`;
+      }
+    }
+  }
+  return code;
+}
+export {
+  provinceAndCityData,
+  regionData,
+  provinceAndCityDataPlus,
+  regionDataPlus,
+  CodeToText,
+  TextToCode,
+  convertTextToCode
+};

+ 148 - 0
src/views/able/cascader.vue

@@ -0,0 +1,148 @@
+<script setup lang="ts">
+import {
+  provinceAndCityDataPlus,
+  provinceAndCityData,
+  convertTextToCode,
+  regionDataPlus,
+  regionData,
+  CodeToText
+} from "/@/utils/chinaArea";
+import { ref } from "vue";
+
+const selectedOptions1 = ref(["110000", "110100"]);
+const selectedOptions2 = ref(["120000", "120100", "120101"]);
+const selectedOptions3 = ref(["130000", ""]);
+const selectedOptions4 = ref(["120000", "120100", ""]);
+
+const handleChange = value => {
+  console.log(value);
+};
+</script>
+
+<template>
+  <el-card>
+    <template #header> 区域级联选择器 </template>
+    <el-row :gutter="20">
+      <el-col :xl="12" :lg="12" :md="24" :sm="24" :xs="24">
+        <div class="flex flex-col items-center justify-center">
+          <span class="imp">
+            1. 二级联动(不带“全部”选项)
+            <el-cascader
+              :options="provinceAndCityData"
+              v-model="selectedOptions1"
+              @change="handleChange"
+            />
+          </span>
+          <div class="leading-10">
+            <div>绑定值:{{ selectedOptions1 }}</div>
+            <div>
+              区域码转汉字:{{ CodeToText[selectedOptions1[0]] }},{{
+                CodeToText[selectedOptions1[1]]
+              }}
+            </div>
+            <div>
+              汉字转区域码:{{
+                convertTextToCode(
+                  CodeToText[selectedOptions1[0]],
+                  CodeToText[selectedOptions1[1]]
+                )
+              }}
+            </div>
+          </div>
+        </div>
+      </el-col>
+      <el-col :xl="12" :lg="12" :md="24" :sm="24" :xs="24">
+        <div class="flex flex-col items-center justify-center mt-3">
+          <span class="imp">
+            2. 二级联动(带有“全部”选项)
+            <el-cascader
+              :options="provinceAndCityDataPlus"
+              v-model="selectedOptions3"
+              @change="handleChange"
+            />
+          </span>
+          <div class="leading-10">
+            <div>绑定值:{{ selectedOptions3 }}</div>
+            <div>
+              区域码转汉字:{{ CodeToText[selectedOptions3[0]] }},{{
+                CodeToText[selectedOptions3[1]]
+              }}
+            </div>
+            <div>
+              汉字转区域码:{{
+                convertTextToCode(
+                  CodeToText[selectedOptions3[0]],
+                  CodeToText[selectedOptions3[1]]
+                )
+              }}
+            </div>
+          </div>
+        </div>
+      </el-col>
+      <el-col :xl="12" :lg="12" :md="24" :sm="24" :xs="24">
+        <div class="flex flex-col items-center justify-center mt-3">
+          <span class="imp">
+            3. 三级联动(不带“全部”选项)
+            <el-cascader
+              :options="regionData"
+              v-model="selectedOptions2"
+              @change="handleChange"
+            />
+          </span>
+          <div class="leading-10">
+            <div>绑定值:{{ selectedOptions2 }}</div>
+            <div>
+              区域码转汉字:{{ CodeToText[selectedOptions2[0]] }},{{
+                CodeToText[selectedOptions2[1]]
+              }},{{ CodeToText[selectedOptions2[2]] }}
+            </div>
+            <div>
+              汉字转区域码:{{
+                convertTextToCode(
+                  CodeToText[selectedOptions2[0]],
+                  CodeToText[selectedOptions2[1]],
+                  CodeToText[selectedOptions2[2]]
+                )
+              }}
+            </div>
+          </div>
+        </div>
+      </el-col>
+      <el-col :xl="12" :lg="12" :md="24" :sm="24" :xs="24">
+        <div class="flex flex-col items-center justify-center mt-3">
+          <span class="imp">
+            4. 三级联动(带"全部选项")
+            <el-cascader
+              :options="regionDataPlus"
+              v-model="selectedOptions4"
+              @change="handleChange"
+            />
+          </span>
+          <div class="leading-10">
+            <div>绑定值:{{ selectedOptions4 }}</div>
+            <div>
+              区域码转汉字:{{ CodeToText[selectedOptions4[0]] }},{{
+                CodeToText[selectedOptions4[1]]
+              }},{{ CodeToText[selectedOptions4[2]] }}
+            </div>
+            <div>
+              汉字转区域码:{{
+                convertTextToCode(
+                  CodeToText[selectedOptions4[0]],
+                  CodeToText[selectedOptions4[1]],
+                  CodeToText[selectedOptions4[2]]
+                )
+              }}
+            </div>
+          </div>
+        </div>
+      </el-col>
+    </el-row>
+  </el-card>
+</template>
+
+<style scoped lang="scss">
+.imp {
+  color: var(--el-color-primary);
+}
+</style>

+ 0 - 1
src/views/list/card/components/DialogForm.vue

@@ -116,7 +116,6 @@ const rules = {
         <el-select
           v-model="formData.type"
           clearable
-          :teleported="false"
           :style="{ width: '480px' }"
         >
           <el-option