VUE-文档计算
499字约2分钟
2024-11-02
::: vue-playground VUE-文档计算
@file spreadSheetStore.ts
import { type Reactive, reactive } from 'vue'
const COLS = 3
const ROWS = 3
export const cells: Reactive<string[][]> = reactive(
Array.from(Array(COLS).keys()).map(() =>
Array.from(Array(ROWS).keys()).map(() => '')
)
)
// initial state for demo
cells[0][0] = '1'
cells[0][1] = '2'
cells[0][2] = '= A0 + A1'
// adapted from https://codesandbox.io/s/jotai-7guis-task7-cells-mzoit?file=/src/atoms.ts
// by @dai-shi
export function evalCell(exp: string) {
if (!exp.startsWith('=')) {
return exp
}
// = A1 + B2 ---> get(0,1) + get(1,2)
exp = exp
.slice(1)
.replace(
/\b([A-Z])(\d{1,2})\b/g,
(_: any, c: string, r: any) => `get(${c.charCodeAt(0) - 65},${r})`
)
try {
return new Function('get', `return ${exp}`)(getCellValue)
} catch (e) {
return `#ERROR ${e}`
}
}
function getCellValue(c: number, r: number) {
const val = evalCell(cells[c][r])
const num = Number(val)
return Number.isFinite(num) ? num : val
}
@file SpreadSheetCell.vue
<script setup lang="ts">
import { ref } from "vue";
import { cells, evalCell } from "./spreadSheetStore.ts";
interface CellProps {
c: number;
r: number;
}
type ElType = {
el: HTMLInputElement;
};
const props = withDefaults(defineProps<CellProps>(), {
c: 0,
r: 0,
});
const editing = ref(false);
function update(e: Event) {
editing.value = false;
if (typeof props.c !== "undefined" && typeof props.r !== "undefined") {
const target = e.target as HTMLInputElement;
cells[props.c][props.r] = target.value.trim();
}
}
</script>
<template>
<div class="cell" :title="cells[c][r]" @click="editing = true">
<input
v-if="editing"
:value="cells[c][r]"
@change="update"
@blur="update"
@vue:mounted="({ el }: ElType) => el.focus()"
/>
<span v-else>{{ evalCell(cells[c][r]) }}</span>
</div>
</template>
<style scoped>
.cell,
.cell input {
height: 1.5em;
line-height: 1.5;
font-size: 15px;
color: #000000;
}
.cell span {
padding: 0 6px;
}
.cell input {
width: 100%;
box-sizing: border-box;
padding: 0 4px;
}
.cell input:focus {
border: 1px solid #f1f1f1;
color: #000000;
}
</style>
@file App.vue
<script setup lang="ts">
import Cell from "./SpreadSheetCell.vue";
import { cells } from "./spreadSheetStore.ts";
const cols = cells.map((_, i) => String.fromCharCode(65 + i));
</script>
<template>
<table>
<thead>
<tr>
<th></th>
<th v-for="c in cols">{{ c }}</th>
</tr>
</thead>
<tbody>
<tr v-for="i in cells[0].length">
<th>{{ i - 1 }}</th>
<td v-for="j in cols.length">
<Cell :r="i - 1" :c="j - 1"></Cell>
</td>
</tr>
</tbody>
</table>
</template>
<style scoped>
th {
color: #000000;
background-color: #f1f1f1;
padding: 0 1em;
}
tr:first-of-type th {
width: 100px;
}
tr:first-of-type th:first-of-type {
width: 25px;
}
td {
border: 1px solid #3c3c3c4a;
padding: 0;
}
</style>
@setting
{
"showCompileOutput": false
}
:::
表格行列间的计算
new Function('get', `return ${exp}`)(getCellValue)
具体步骤如下:
- 使用
new Function
创建一个匿名函数,该函数接受一个参数 get。 - 匿名函数的函数体为
return ${exp}
,其中exp
是一个字符串表达式。 - 执行这个匿名函数,并将
getCellValue
传递给它作为参数。