소스 검색

feature: formula公式计算器

xiongxt 1 년 전
부모
커밋
8f3c4340ef
16개의 변경된 파일191개의 추가작업 그리고 14개의 파일을 삭제
  1. 1 0
      .eslintrc.js
  2. 43 0
      lib/formula.js
  3. 1 0
      lib/index.js
  4. 48 0
      sdk/formula.ts
  5. 1 0
      sdk/index.ts
  6. 9 0
      src/App.vue
  7. 18 7
      src/components/leftAside.vue
  8. 9 2
      src/router/index.js
  9. 1 1
      src/views/async.vue
  10. 1 1
      src/views/flexible.vue
  11. 42 0
      src/views/formula.vue
  12. 1 1
      src/views/http.vue
  13. 1 1
      src/views/queue.vue
  14. 1 1
      src/views/storage.vue
  15. 13 0
      types/formula.d.ts
  16. 1 0
      types/index.d.ts

+ 1 - 0
.eslintrc.js

@@ -30,6 +30,7 @@ const config = {
     "vue/multi-word-component-names": "off",
     "@typescript-eslint/no-empty-function": "off",
     "@typescript-eslint/no-unused-vars": "off",
+    "no-useless-escape": "off",
   },
 };
 

+ 43 - 0
lib/formula.js

@@ -0,0 +1,43 @@
+class Formula {
+    constructor(_formula) {
+        this.formula = "";
+        this.watchers = [];
+        this.ast = [];
+        this.formula = _formula;
+        this.compile();
+    }
+    compile() {
+        const ast = this.formula.split(/([\+\-\*\/\(\)])/);
+        this.ast = ast.filter((it) => it !== "");
+        this.ast.forEach((char) => {
+            if (this.isSymbol(char) || this.isStatic(char)) {
+                return;
+            }
+            this.watchers.push(char.split(/[\.\[]/)[0]);
+        });
+    }
+    isSymbol(char) {
+        return /^[\+\-\*\/\(\)]{1}$/g.test(char);
+    }
+    isStatic(char) {
+        return /[\"\']/.test(char) || /^\d+$/.test(char);
+    }
+    shouldUpdate(watcher) {
+        return this.watchers.includes(watcher);
+    }
+    result(_context) {
+        const ast = this.ast.map((char) => {
+            if (this.isSymbol(char) || this.isStatic(char)) {
+                return char;
+            }
+            return char.replace(/(^[\.\[]*)/, "_context.$1");
+        });
+        return eval(ast.join(""));
+    }
+    destroy() {
+        this.formula = "";
+        this.watchers = [];
+        this.ast = [];
+    }
+}
+export { Formula };

+ 1 - 0
lib/index.js

@@ -3,3 +3,4 @@ export * from "./async";
 export * from "./storage";
 export * from "./flexible";
 export * from "./http";
+export * from "./formula";

+ 48 - 0
sdk/formula.ts

@@ -0,0 +1,48 @@
+/**
+ * 公式编译
+ */
+class Formula {
+  private formula = "";
+  private watchers: string[] = [];
+  private ast: string[] = [];
+  constructor(_formula) {
+    this.formula = _formula;
+    this.compile();
+  }
+  private compile() {
+    // eslint-disable-next-line no-useless-escape
+    const ast = this.formula.split(/([\+\-\*\/\(\)])/);
+    this.ast = ast.filter((it) => it !== "");
+    this.ast.forEach((char) => {
+      if (this.isSymbol(char) || this.isStatic(char)) {
+        return;
+      }
+      this.watchers.push(char.split(/[\.\[]/)[0]);
+    });
+  }
+  private isSymbol(char: string) {
+    return /^[\+\-\*\/\(\)]{1}$/g.test(char);
+  }
+  private isStatic(char: string) {
+    return /[\"\']/.test(char) || /^\d+$/.test(char);
+  }
+  shouldUpdate(watcher: string) {
+    return this.watchers.includes(watcher);
+  }
+  result(_context: NonNullable<unknown>) {
+    const ast = this.ast.map((char) => {
+      if (this.isSymbol(char) || this.isStatic(char)) {
+        return char;
+      }
+      return char.replace(/(^[\.\[]*)/, "_context.$1");
+    });
+    return eval(ast.join(""));
+  }
+  destroy() {
+    this.formula = "";
+    this.watchers = [];
+    this.ast = [];
+  }
+}
+
+export { Formula };

+ 1 - 0
sdk/index.ts

@@ -3,3 +3,4 @@ export * from "./async";
 export * from "./storage";
 export * from "./flexible";
 export * from "./http";
+export * from "./formula";

+ 9 - 0
src/App.vue

@@ -32,6 +32,15 @@ body {
   /* prettier-ignore */
   min-width: 700PX;
 }
+
+.run,
+.demo {
+  padding-bottom: 10px;
+  > button {
+    margin-right: 10px;
+    cursor: pointer;
+  }
+}
 </style>
 
 <style lang="less" scoped>

+ 18 - 7
src/components/leftAside.vue

@@ -1,19 +1,30 @@
 <template>
   <ul class="left-aside">
-    <li>
-      <RouterLink to="/sdk/queue">Queue队列</RouterLink>
+    <li v-for="it in subRoutes" :key="it.name">
+      <RouterLink :to="it.name">{{ it.name }}</RouterLink>
     </li>
-    <li><RouterLink to="/sdk/async">Async异步</RouterLink></li>
-    <li><RouterLink to="/sdk/storage">Storage</RouterLink></li>
-    <li><RouterLink to="/sdk/flexible">flexible</RouterLink></li>
-    <li><RouterLink to="/sdk/http">Http请求</RouterLink></li>
   </ul>
 </template>
 
 <script>
+import { computed } from "vue";
+import { useRoute } from "vue-router";
 export default {
   name: "LeftAside",
-  setup() {},
+  setup() {
+    const route = useRoute();
+    const subRoutes = computed(() => {
+      const matched =
+        route.matched[route.matched.length - 2] || route.matched[0];
+      if ((route.matched, matched)) {
+        return matched.children.filter(
+          (it) => !["", "/:pathMatch(.*)"].includes(it.path)
+        );
+      }
+      return [];
+    });
+    return { subRoutes };
+  },
 };
 </script>
 

+ 9 - 2
src/router/index.js

@@ -8,8 +8,9 @@ const routes = [
   {
     path: "/",
     name: "home",
-    component: () =>
-      import(/* webpackChunkName: "about" */ "../views/queue.vue"),
+    redirect: {
+      name: "sdk",
+    },
   },
   {
     path: "/sdk",
@@ -51,6 +52,12 @@ const routes = [
         component: () =>
           import(/* webpackChunkName: "http" */ "../views/http.vue"),
       },
+      {
+        path: "formula",
+        name: "formula",
+        component: () =>
+          import(/* webpackChunkName: "formula" */ "../views/formula.vue"),
+      },
       {
         path: "",
         redirect: {

+ 1 - 1
src/views/async.vue

@@ -4,7 +4,7 @@
       title="Async"
       desc="这里主要是异步遍历的函数集合,函数会等待上一次异步结束才会开始下一个"
     >
-      <p>DEMO</p>
+      <p class="demo">DEMO</p>
       <Codemirror v-model="code" :disabled="true" />
     </Block>
   </div>

+ 1 - 1
src/views/flexible.vue

@@ -4,7 +4,7 @@
       title="flexible"
       desc="amfe-flexible代码变种,取消了body的font-size,新增了配置最低宽度的选项"
     >
-      <p>DEMO</p>
+      <p class="demo">DEMO</p>
       <Codemirror v-model="code" :disabled="true" />
     </Block>
   </div>

+ 42 - 0
src/views/formula.vue

@@ -0,0 +1,42 @@
+<template>
+  <div>
+    <Block title="formula" desc="手动运行字符串公式">
+      <p class="run">
+        <button @click="execute">运行</button>请在控制台查看结果
+      </p>
+      <p class="demo">DEMO</p>
+      <Codemirror v-model="code" :disabled="true" />
+    </Block>
+  </div>
+</template>
+
+<script setup>
+import Block from "@/components/block.vue";
+import { ref } from "vue";
+import { Codemirror } from "vue-codemirror";
+
+import { Formula } from "../../lib";
+
+function execute() {
+  const f = new Formula("(a[0]+b.x)/c");
+  console.log(
+    f.result({
+      a: [3],
+      b: { x: 9 },
+      c: 9,
+    })
+  );
+  f.destroy();
+}
+
+const code = ref(`import { Formula } from "ludash";
+const f = new Formula("(a[0]+b.x)/c");
+console.log(
+  f.result({
+    a: [3],
+    b: { x: 9 },
+    c: 9,
+  })
+);
+f.destroy();`);
+</script>

+ 1 - 1
src/views/http.vue

@@ -4,7 +4,7 @@
       title="Http"
       desc="根据使用习惯将axios进行了二次封装,提供了更方便的cancel方法,"
     >
-      <p>DEMO</p>
+      <p class="demo">DEMO</p>
       <Codemirror v-model="code" :disabled="true" />
     </Block>
   </div>

+ 1 - 1
src/views/queue.vue

@@ -4,7 +4,7 @@
       title="Queue"
       desc="这个函数用于创建一个promise队列,并按加入顺序执行"
     >
-      <p>DEMO</p>
+      <p class="demo">DEMO</p>
       <Codemirror v-model="code" :disabled="true" />
     </Block>
   </div>

+ 1 - 1
src/views/storage.vue

@@ -4,7 +4,7 @@
       title="Storage"
       desc="将浏览器的LocalStorage和SessionStorage重新包装了一下"
     >
-      <p>DEMO</p>
+      <p class="demo">DEMO</p>
       <Codemirror v-model="code" :disabled="true" />
     </Block>
   </div>

+ 13 - 0
types/formula.d.ts

@@ -0,0 +1,13 @@
+declare class Formula {
+    private formula;
+    private watchers;
+    private ast;
+    constructor(_formula: any);
+    private compile;
+    private isSymbol;
+    private isStatic;
+    shouldUpdate(watcher: string): boolean;
+    result(_context: NonNullable<unknown>): any;
+    destroy(): void;
+}
+export { Formula };

+ 1 - 0
types/index.d.ts

@@ -3,3 +3,4 @@ export * from "./async";
 export * from "./storage";
 export * from "./flexible";
 export * from "./http";
+export * from "./formula";