mirror of
https://gitee.com/orangeform/orange-admin.git
synced 2026-01-17 10:36:31 +08:00
commit:升级到vue3,更新最近工作流技术栈,支持sa-token
This commit is contained in:
240
OrangeFormsOpen-VUE3/src/components/AdvanceQuery/index.vue
Normal file
240
OrangeFormsOpen-VUE3/src/components/AdvanceQuery/index.vue
Normal file
@@ -0,0 +1,240 @@
|
||||
<template>
|
||||
<el-container class="advance-query-form advance-query advance-box">
|
||||
<el-aside width="300px" style="background-color: white">
|
||||
<el-card class="base-card" shadow="never" style="height: 100%; border: none">
|
||||
<template v-slot:header>
|
||||
<div class="base-card-header">
|
||||
<span class="header-title">{{ treePanel.title }}</span>
|
||||
<div class="base-card-operation">
|
||||
<el-button
|
||||
class="advance-icon-btn"
|
||||
@click="refreshGroup()"
|
||||
style="width: 40px; height: 32px; padding: 0"
|
||||
>
|
||||
<img src="@/assets/img/refresh2.png" alt="" style="vertical-align: middle" />
|
||||
</el-button>
|
||||
<el-button
|
||||
class="advance-icon-btn"
|
||||
v-if="treePanel.supportAdd"
|
||||
style="width: 40px; height: 32px; padding: 0"
|
||||
@click="onEditGroupItem(null)"
|
||||
>
|
||||
<!-- {{treePanel.addText}} -->
|
||||
<img src="@/assets/img/add.png" alt="" style="vertical-align: middle" />
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-scrollbar :style="{ height: height - 130 + 'px' }" class="custom-scroll">
|
||||
<el-tree
|
||||
ref="groupTree"
|
||||
:data="groupDataList"
|
||||
:node-key="treePanel.keyColumnName"
|
||||
@node-click="onNodeClick"
|
||||
:highlight-current="true"
|
||||
:default-expand-all="true"
|
||||
:auto-expand-parent="true"
|
||||
:expand-on-click-node="false"
|
||||
>
|
||||
<template v-slot="{ data }">
|
||||
<el-row
|
||||
class="tree-node-item module-node-item"
|
||||
type="flex"
|
||||
justify="space-between"
|
||||
align="middle"
|
||||
>
|
||||
<span class="node-text" :title="data[treePanel.nameColumnName]">{{
|
||||
data[treePanel.nameColumnName]
|
||||
}}</span>
|
||||
<div class="module-node-menu" style="padding-right: 9px">
|
||||
<el-button
|
||||
v-if="treePanel.supportEdit"
|
||||
link
|
||||
type="primary"
|
||||
@click.stop="onEditGroupItem(data)"
|
||||
:icon="Edit"
|
||||
></el-button>
|
||||
<el-button
|
||||
v-if="treePanel.supportDelete"
|
||||
link
|
||||
type="danger"
|
||||
@click.stop="onDeleteGroupItem(data)"
|
||||
:icon="Delete"
|
||||
></el-button>
|
||||
</div>
|
||||
</el-row>
|
||||
</template>
|
||||
</el-tree>
|
||||
</el-scrollbar>
|
||||
</el-card>
|
||||
</el-aside>
|
||||
<el-main class="table-panel">
|
||||
<el-card
|
||||
class="base-card"
|
||||
shadow="never"
|
||||
:body-style="{ padding: '0px' }"
|
||||
style="border: none"
|
||||
>
|
||||
<template v-slot:header>
|
||||
<div class="base-card-header">
|
||||
<span class="header-title">{{ tablePanel.title }}</span>
|
||||
<div class="base-card-operation">
|
||||
<slot name="tableFilter" />
|
||||
<el-button
|
||||
v-if="tablePanel.supportAdd"
|
||||
type="primary"
|
||||
style="margin-left: 20px"
|
||||
:size="layoutStore.defaultFormItemSize"
|
||||
:icon="Plus"
|
||||
@click="onAddTableItem()"
|
||||
>{{ tablePanel.addText }}</el-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div class="advance-table-box">
|
||||
<slot name="table" />
|
||||
</div>
|
||||
</el-card>
|
||||
</el-main>
|
||||
<slot />
|
||||
</el-container>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Edit, Delete, Plus } from '@element-plus/icons-vue';
|
||||
import { inject, onMounted, ref, watch } from 'vue';
|
||||
import { ANY_OBJECT } from '@/types/generic';
|
||||
import { treeDataTranslate, findItemFromList } from '@/common/utils';
|
||||
|
||||
const emit = defineEmits<{
|
||||
editGroupItem: [ANY_OBJECT];
|
||||
refreshTable: [ANY_OBJECT];
|
||||
deleteGroupItem: [ANY_OBJECT];
|
||||
addTableItem: [];
|
||||
}>();
|
||||
|
||||
const props = defineProps<{
|
||||
height: number;
|
||||
treePanel: ANY_OBJECT;
|
||||
tablePanel: ANY_OBJECT;
|
||||
}>();
|
||||
|
||||
import { useLayoutStore } from '@/store';
|
||||
const layoutStore = useLayoutStore();
|
||||
|
||||
const groupTree = ref();
|
||||
const currentGroup = ref();
|
||||
const groupDataList = ref<ANY_OBJECT[]>([]);
|
||||
|
||||
const loadGroupData = () => {
|
||||
if (typeof props.treePanel.loadFunction === 'function') {
|
||||
props.treePanel
|
||||
.loadFunction()
|
||||
.then((dataList: ANY_OBJECT[]) => {
|
||||
groupDataList.value = treeDataTranslate(
|
||||
dataList.map((item: ANY_OBJECT) => {
|
||||
return {
|
||||
...item,
|
||||
};
|
||||
}),
|
||||
props.treePanel.keyColumnName,
|
||||
);
|
||||
if (currentGroup.value != null) {
|
||||
currentGroup.value = findItemFromList(
|
||||
dataList,
|
||||
currentGroup.value[props.treePanel.keyColumnName],
|
||||
props.treePanel.keyColumnName,
|
||||
);
|
||||
}
|
||||
if (currentGroup.value == null) currentGroup.value = dataList[0];
|
||||
setTimeout(() => {
|
||||
if (groupTree.value)
|
||||
groupTree.value.setCurrentKey(currentGroup.value[props.treePanel.keyColumnName]);
|
||||
}, 50);
|
||||
})
|
||||
.catch((e: Error) => {
|
||||
console.warn(e);
|
||||
});
|
||||
}
|
||||
};
|
||||
const refreshGroup = () => {
|
||||
loadGroupData();
|
||||
};
|
||||
const onEditGroupItem = (data: ANY_OBJECT) => {
|
||||
emit('editGroupItem', data);
|
||||
};
|
||||
const onNodeClick = (data: ANY_OBJECT) => {
|
||||
currentGroup.value = data;
|
||||
};
|
||||
const onDeleteGroupItem = (data: ANY_OBJECT) => {
|
||||
emit('deleteGroupItem', data);
|
||||
};
|
||||
const onAddTableItem = () => {
|
||||
emit('addTableItem');
|
||||
};
|
||||
|
||||
defineExpose({ refreshGroup });
|
||||
|
||||
watch(
|
||||
currentGroup,
|
||||
(newVal, oldVal) => {
|
||||
if (newVal != oldVal) {
|
||||
emit('refreshTable', newVal);
|
||||
}
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
},
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
loadGroupData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.el-main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.table-panel {
|
||||
padding: 0;
|
||||
margin-left: 15px;
|
||||
background: white;
|
||||
}
|
||||
.table-panel .base-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
}
|
||||
.advance-query :deep(.el-card__body) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
}
|
||||
.advance-query :deep(.el-tree-node__content) {
|
||||
height: 35px;
|
||||
}
|
||||
.advance-query .tree-node-item {
|
||||
flex: 1;
|
||||
height: 35px;
|
||||
line-height: 35px;
|
||||
font-size: 12px;
|
||||
}
|
||||
.advance-query .module-node-menu {
|
||||
text-align: right;
|
||||
}
|
||||
.advance-query .tree-node-item .node-text {
|
||||
overflow: hidden;
|
||||
max-width: 175px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
flex: 1;
|
||||
}
|
||||
.advance-table-box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
}
|
||||
</style>
|
||||
44
OrangeFormsOpen-VUE3/src/components/Btns/RightAddBtn.vue
Normal file
44
OrangeFormsOpen-VUE3/src/components/Btns/RightAddBtn.vue
Normal file
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<el-button
|
||||
v-bind="$attrs"
|
||||
type="primary"
|
||||
class="right-add-btn"
|
||||
:size="layoutStore.defaultFormItemSize"
|
||||
link
|
||||
:icon="CirclePlusFilled"
|
||||
>
|
||||
<slot />
|
||||
</el-button>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { CirclePlusFilled } from '@element-plus/icons-vue';
|
||||
import { EpPropMergeType } from 'element-plus/es/utils';
|
||||
import { useLayoutStore } from '@/store';
|
||||
const layoutStore = useLayoutStore();
|
||||
|
||||
type BtnSizeType =
|
||||
| EpPropMergeType<StringConstructor, '' | 'default' | 'small' | 'large', never>
|
||||
| undefined;
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
size?: BtnSizeType;
|
||||
}>(),
|
||||
{ size: 'default' },
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.right-add-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
:deep(.el-icon-circle-plus) {
|
||||
font-size: 14px !important;
|
||||
}
|
||||
|
||||
:deep(span) {
|
||||
margin-left: 4px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
271
OrangeFormsOpen-VUE3/src/components/DateRange/index.vue
Normal file
271
OrangeFormsOpen-VUE3/src/components/DateRange/index.vue
Normal file
@@ -0,0 +1,271 @@
|
||||
<template>
|
||||
<div class="date-range">
|
||||
<el-select
|
||||
v-model="dateType"
|
||||
:size="size"
|
||||
style="min-width: 100px; max-width: 100px; margin-right: 10px"
|
||||
v-if="!hideTypeOnlyOne || validTypeList.length > 1"
|
||||
>
|
||||
<el-option
|
||||
v-for="type in validTypeList"
|
||||
:key="type.value"
|
||||
:value="type.value"
|
||||
:label="type.label"
|
||||
/>
|
||||
</el-select>
|
||||
<el-date-picker
|
||||
style="flex-grow: 1"
|
||||
v-model="currentDates"
|
||||
:size="size"
|
||||
:placeholder="placeholder"
|
||||
:type="innerDateType"
|
||||
:disabled="disabled"
|
||||
:format="innerDateFormat"
|
||||
:readonly="readonly"
|
||||
:editable="editable"
|
||||
:clearable="clearable"
|
||||
:start-placeholder="startPlaceholder"
|
||||
:end-placeholder="endPlaceholder"
|
||||
:align="align"
|
||||
:range-separator="rangeSeparator"
|
||||
:value-format="valueFormat"
|
||||
:prefix-icon="prefixIcon"
|
||||
:clear-icon="clearIcon"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, Component } from 'vue';
|
||||
import { CircleClose, Calendar } from '@element-plus/icons-vue';
|
||||
import { ANY_OBJECT } from '@/types/generic';
|
||||
|
||||
type ModelValue = string | Date | [Date, Date] | [string, string];
|
||||
|
||||
const allTypeList = [
|
||||
{
|
||||
value: 'day',
|
||||
label: '自然日',
|
||||
},
|
||||
{
|
||||
value: 'month',
|
||||
label: '自然月',
|
||||
},
|
||||
{
|
||||
value: 'year',
|
||||
label: '自然年',
|
||||
},
|
||||
];
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:value': [ModelValue];
|
||||
change: [ModelValue];
|
||||
}>();
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
value?: ModelValue;
|
||||
// 默认显示的数据选择方式,如果不存在与allowTypes中则显示allowTypes中的第一个
|
||||
defaultDateType?: string;
|
||||
// small / default / large
|
||||
size?: '' | 'default' | 'small' | 'large' | undefined;
|
||||
// 数据选择方式只有一个的时候是否隐藏数据选择方式下拉
|
||||
hideTypeOnlyOne?: boolean;
|
||||
// 允许的数据选择方式(day, month, year)
|
||||
allowTypes?: string[];
|
||||
// 是否范围选择
|
||||
isRange?: boolean;
|
||||
editable?: boolean;
|
||||
clearable?: boolean;
|
||||
// 对齐方式(left, center, right)
|
||||
align?: string;
|
||||
rangeSeparator?: string;
|
||||
format?: string;
|
||||
valueFormat?: string;
|
||||
// 自定义头部图标的类名
|
||||
prefixIcon?: Component;
|
||||
// 自定义清空图标的类名
|
||||
clearIcon?: Component;
|
||||
// 非范围选择时的占位内容
|
||||
placeholder?: string;
|
||||
// 范围选择时开始日期的占位内容
|
||||
startPlaceholder?: string;
|
||||
//范围选择时结束日期的占位内容
|
||||
endPlaceholder?: string;
|
||||
// 完全只读
|
||||
readonly?: boolean;
|
||||
//禁用
|
||||
disabled?: boolean;
|
||||
}>(),
|
||||
{
|
||||
value: () => '',
|
||||
defaultDateType: 'day',
|
||||
size: 'default',
|
||||
hideTypeOnlyOne: true,
|
||||
allowTypes: () => ['day', 'month', 'year'],
|
||||
isRange: true,
|
||||
editable: true,
|
||||
clearable: true,
|
||||
align: 'left',
|
||||
rangeSeparator: '-',
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
prefixIcon: Calendar,
|
||||
clearIcon: CircleClose,
|
||||
},
|
||||
);
|
||||
|
||||
const dateType = ref(props.defaultDateType);
|
||||
const currentDates = computed({
|
||||
get() {
|
||||
return props.value;
|
||||
},
|
||||
set(val) {
|
||||
emit('update:value', val);
|
||||
},
|
||||
});
|
||||
|
||||
const emitChange = () => {
|
||||
let outputDate = [];
|
||||
if (currentDates.value != null) {
|
||||
if (!props.isRange) {
|
||||
outputDate[0] = new Date(currentDates.value.toString());
|
||||
outputDate[1] = new Date(currentDates.value.toString());
|
||||
} else {
|
||||
if (Array.isArray(currentDates.value) && currentDates.value.length == 2) {
|
||||
outputDate[0] = new Date(currentDates.value[0]);
|
||||
outputDate[1] = new Date(currentDates.value[1]);
|
||||
}
|
||||
}
|
||||
|
||||
if (outputDate[0] != null && outputDate[1] != null) {
|
||||
outputDate[0].setHours(0, 0, 0, 0);
|
||||
outputDate[1].setHours(0, 0, 0, 0);
|
||||
switch (dateType.value) {
|
||||
case 'day':
|
||||
outputDate[1].setDate(outputDate[1].getDate() + 1);
|
||||
break;
|
||||
case 'month':
|
||||
outputDate[1].setDate(1);
|
||||
outputDate[0].setDate(1);
|
||||
outputDate[1].setMonth(outputDate[1].getMonth() + 1);
|
||||
break;
|
||||
case 'year':
|
||||
outputDate[1].setMonth(0);
|
||||
outputDate[1].setDate(1);
|
||||
outputDate[0].setMonth(0);
|
||||
outputDate[0].setDate(1);
|
||||
outputDate[1].setFullYear(outputDate[1].getFullYear() + 1);
|
||||
break;
|
||||
}
|
||||
outputDate[1] = new Date(outputDate[1].getTime() - 1);
|
||||
}
|
||||
}
|
||||
emit('update:value', outputDate as ModelValue);
|
||||
emit('change', outputDate as ModelValue);
|
||||
};
|
||||
|
||||
const validTypeList = computed(() => {
|
||||
return allTypeList.filter(item => {
|
||||
return props.allowTypes.indexOf(item.value) != -1;
|
||||
});
|
||||
});
|
||||
/**
|
||||
* el-date-picker使用的type
|
||||
*/
|
||||
const innerDateType = computed(() => {
|
||||
switch (dateType.value) {
|
||||
case 'day':
|
||||
return props.isRange ? 'daterange' : 'date';
|
||||
case 'month':
|
||||
return props.isRange ? 'monthrange' : 'month';
|
||||
case 'year':
|
||||
return props.isRange ? 'monthrange' : 'year';
|
||||
default:
|
||||
return props.isRange ? 'daterange' : 'date';
|
||||
}
|
||||
});
|
||||
/**
|
||||
* el-date-picker使用的format
|
||||
*/
|
||||
const innerDateFormat = computed(() => {
|
||||
switch (dateType.value) {
|
||||
case 'day':
|
||||
return 'YYYY-MM-DD';
|
||||
case 'month':
|
||||
return 'YYYY-MM';
|
||||
case 'year':
|
||||
return 'YYYY';
|
||||
default:
|
||||
return 'YYYY-MM-DD';
|
||||
}
|
||||
});
|
||||
|
||||
watch(
|
||||
dateType,
|
||||
(newValue, oldValue) => {
|
||||
console.log('daterange dateType changed', newValue, oldValue);
|
||||
if (props.allowTypes.indexOf(dateType.value) == -1) {
|
||||
dateType.value = props.allowTypes[0] || 'day';
|
||||
}
|
||||
emitChange();
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true,
|
||||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.value,
|
||||
(newVal, oldVal) => {
|
||||
console.log('daterange value change', newVal, oldVal);
|
||||
if (newVal != oldVal) {
|
||||
currentDates.value = newVal;
|
||||
}
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.defaultDateType,
|
||||
(newValue, oldValue) => {
|
||||
console.log('daterange defaultDateType changed', newValue, oldValue);
|
||||
if (props.allowTypes.indexOf(newValue) !== -1) {
|
||||
dateType.value = newValue;
|
||||
} else {
|
||||
dateType.value = props.allowTypes[0];
|
||||
}
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true,
|
||||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.isRange,
|
||||
(newValue, oldValue) => {
|
||||
console.log('daterange isRange changed', newValue, oldValue);
|
||||
if (newValue) {
|
||||
if (currentDates.value && !Array.isArray(currentDates.value)) {
|
||||
currentDates.value = [currentDates.value.toString(), currentDates.value.toString()];
|
||||
}
|
||||
} else if (Array.isArray(currentDates.value)) {
|
||||
currentDates.value = currentDates.value[0] as ModelValue;
|
||||
}
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true,
|
||||
},
|
||||
);
|
||||
</script>
|
||||
|
||||
<style type="scss" scoped>
|
||||
.date-range {
|
||||
display: flex;
|
||||
}
|
||||
</style>
|
||||
240
OrangeFormsOpen-VUE3/src/components/DeptSelect/DeptSelectDlg.vue
Normal file
240
OrangeFormsOpen-VUE3/src/components/DeptSelect/DeptSelectDlg.vue
Normal file
@@ -0,0 +1,240 @@
|
||||
<template>
|
||||
<div style="width: 100%">
|
||||
<el-form
|
||||
ref="formSysDept"
|
||||
:model="formFilter"
|
||||
label-width="75px"
|
||||
size="default"
|
||||
label-position="right"
|
||||
@submit.prevent
|
||||
>
|
||||
<el-row type="flex" justify="space-between">
|
||||
<el-form-item label="部门名称">
|
||||
<el-input
|
||||
v-model="formFilter.deptName"
|
||||
style="width: 200px"
|
||||
:clearable="true"
|
||||
placeholder="部门名称"
|
||||
@change="refreshFormSysDept(true)"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-button type="primary" @click="onSubmit" style="height: 28px" size="default">
|
||||
确定
|
||||
</el-button>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<vxe-table
|
||||
ref="table"
|
||||
:row-id="dialogParams.props.value"
|
||||
:data="formSysDeptWidget.dataList"
|
||||
height="500"
|
||||
show-overflow="title"
|
||||
show-header-overflow="title"
|
||||
:row-config="{ height: 35, isHover: true }"
|
||||
:radio-config="{ highlight: true }"
|
||||
:checkbox-config="getSelectConfig"
|
||||
:tree-config="{
|
||||
transform: true,
|
||||
rowField: 'deptId',
|
||||
parentField: 'parentId',
|
||||
expandAll: true,
|
||||
}"
|
||||
@radio-change="onSelectChange"
|
||||
@checkbox-all="onSelectChange"
|
||||
@checkbox-change="onSelectChange"
|
||||
>
|
||||
<vxe-column :type="dialogParams.multiple ? 'checkbox' : 'radio'" :width="50" />
|
||||
<vxe-column title="部门名称" field="deptName" tree-node />
|
||||
<template v-slot:empty>
|
||||
<div class="table-empty unified-font">
|
||||
<img src="@/assets/img/empty.png" />
|
||||
<span>暂无数据</span>
|
||||
</div>
|
||||
</template>
|
||||
</vxe-table>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { VxeTable, VxeColumn, VxeTableDefines } from 'vxe-table';
|
||||
import { reactive, ref, nextTick, computed, onMounted } from 'vue';
|
||||
import { ANY_OBJECT } from '@/types/generic';
|
||||
import { DialogProp } from '@/components/Dialog/types';
|
||||
import { useTable } from '@/common/hooks/useTable';
|
||||
import { TableOptions } from '@/common/types/pagination';
|
||||
import { SysCommonBizController } from '@/api/system';
|
||||
import { findItemFromList } from '@/common/utils';
|
||||
import { useThirdParty } from '@/components/thirdParty/hooks';
|
||||
import { ThirdProps } from '@/components/thirdParty/types';
|
||||
|
||||
interface IProps extends ThirdProps {
|
||||
value: Array<ANY_OBJECT>;
|
||||
props?: ANY_OBJECT;
|
||||
multiple?: boolean;
|
||||
// 当使用Dialog.show弹出组件时,须定义该prop属性,以便对dialog进行回调
|
||||
dialog?: DialogProp<ANY_OBJECT | ANY_OBJECT[] | undefined>;
|
||||
}
|
||||
const props = withDefaults(defineProps<IProps>(), {
|
||||
props: () => {
|
||||
return {
|
||||
label: 'deptName',
|
||||
value: 'deptId',
|
||||
};
|
||||
},
|
||||
multiple: false,
|
||||
});
|
||||
|
||||
const { thirdParams, onCloseThirdDialog } = useThirdParty(props);
|
||||
|
||||
const table = ref();
|
||||
const formFilter = reactive<{ deptName: string | undefined }>({ deptName: undefined });
|
||||
const dialogSelectItems = ref<ANY_OBJECT | ANY_OBJECT[]>();
|
||||
|
||||
const dialogParams = computed(() => {
|
||||
return {
|
||||
value: props.value || thirdParams.value.value,
|
||||
props:
|
||||
props.props || thirdParams.value.props == null ? props.props : thirdParams.value.props || {},
|
||||
multiple:
|
||||
props.multiple || thirdParams.value.multiple == null
|
||||
? props.multiple
|
||||
: thirdParams.value.multiple,
|
||||
};
|
||||
});
|
||||
const getSelectConfig = computed(() => {
|
||||
let selectRowKeys =
|
||||
dialogParams.value.multiple || Array.isArray(dialogSelectItems.value)
|
||||
? (dialogSelectItems.value || []).map((item: ANY_OBJECT) => {
|
||||
return item[dialogParams.value.props.value];
|
||||
})
|
||||
: (dialogSelectItems.value || {})[dialogParams.value.props.value];
|
||||
return {
|
||||
highlight: true,
|
||||
checkStrictly: true,
|
||||
checkRowKeys: dialogParams.value.multiple ? selectRowKeys : undefined,
|
||||
checkRowKey: dialogParams.value.multiple ? undefined : selectRowKeys,
|
||||
showHeader: false,
|
||||
};
|
||||
});
|
||||
|
||||
const setTableSelectRow = () => {
|
||||
if (table.value == null || !Array.isArray(formSysDeptWidget.dataList)) return;
|
||||
setTimeout(() => {
|
||||
table.value.clearRadioRow();
|
||||
table.value.clearCheckboxRow();
|
||||
if (dialogParams.value.multiple || Array.isArray(dialogSelectItems.value)) {
|
||||
table.value.setCheckboxRow(
|
||||
formSysDeptWidget.dataList.filter(item => {
|
||||
return (
|
||||
findItemFromList(
|
||||
dialogSelectItems.value as ANY_OBJECT[],
|
||||
item[dialogParams.value.props.value],
|
||||
dialogParams.value.props.value,
|
||||
) != null
|
||||
);
|
||||
}),
|
||||
true,
|
||||
);
|
||||
} else {
|
||||
let selectRow = findItemFromList(
|
||||
formSysDeptWidget.dataList,
|
||||
(dialogSelectItems.value || {})[dialogParams.value.props.value],
|
||||
dialogParams.value.props.value,
|
||||
);
|
||||
table.value.setRadioRow(selectRow);
|
||||
}
|
||||
}, 50);
|
||||
};
|
||||
const loadSysDeptData = (params: ANY_OBJECT) => {
|
||||
params.widgetType = 'upms_dept';
|
||||
params.filter = {
|
||||
deptName: formFilter.deptName,
|
||||
};
|
||||
return new Promise((resolve, reject) => {
|
||||
SysCommonBizController.list(params)
|
||||
.then(res => {
|
||||
resolve({
|
||||
dataList: res.data.dataList,
|
||||
totalCount: res.data.totalCount,
|
||||
});
|
||||
nextTick(() => {
|
||||
setTableSelectRow();
|
||||
});
|
||||
})
|
||||
.catch((e: Error) => {
|
||||
reject(e);
|
||||
});
|
||||
});
|
||||
};
|
||||
const loadSysDeptVerify = () => {
|
||||
return true;
|
||||
};
|
||||
const tableOptions: TableOptions<ANY_OBJECT> = {
|
||||
loadTableData: loadSysDeptData,
|
||||
verifyTableParameter: loadSysDeptVerify,
|
||||
};
|
||||
const formSysDeptWidget = reactive(useTable(tableOptions));
|
||||
|
||||
const onSubmit = () => {
|
||||
if (props.dialog) {
|
||||
props.dialog.submit(dialogSelectItems.value);
|
||||
} else {
|
||||
onCloseThirdDialog(true, dialogParams.value.value, dialogSelectItems.value);
|
||||
}
|
||||
};
|
||||
|
||||
const refreshFormSysDept = (reloadData = false) => {
|
||||
// 重新获取数据组件的数据
|
||||
if (reloadData) {
|
||||
formSysDeptWidget.refreshTable(true, 1);
|
||||
} else {
|
||||
formSysDeptWidget.refreshTable();
|
||||
}
|
||||
};
|
||||
|
||||
const onSelectChange = ({
|
||||
checked,
|
||||
row,
|
||||
}: VxeTableDefines.RadioChangeEventParams | VxeTableDefines.CheckboxAllEventParams) => {
|
||||
if (dialogParams.value.multiple) {
|
||||
if (row == null) {
|
||||
dialogSelectItems.value = [];
|
||||
if (checked) {
|
||||
dialogSelectItems.value = formSysDeptWidget.dataList;
|
||||
}
|
||||
} else {
|
||||
if (dialogSelectItems.value == null) dialogSelectItems.value = [];
|
||||
if (checked) {
|
||||
dialogSelectItems.value.push(row);
|
||||
} else {
|
||||
dialogSelectItems.value = dialogSelectItems.value.filter((item: ANY_OBJECT) => {
|
||||
return item[dialogParams.value.props.value] !== row[dialogParams.value.props.value];
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
dialogSelectItems.value = row;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
if (Array.isArray(dialogParams.value.value) && dialogParams.value.value.length > 0) {
|
||||
if (dialogParams.value.multiple) {
|
||||
dialogSelectItems.value = dialogParams.value.value.map(item => {
|
||||
return {
|
||||
...item,
|
||||
};
|
||||
});
|
||||
} else {
|
||||
dialogSelectItems.value = {
|
||||
...dialogParams.value.value[0],
|
||||
};
|
||||
}
|
||||
}
|
||||
refreshFormSysDept(true);
|
||||
});
|
||||
</script>
|
||||
211
OrangeFormsOpen-VUE3/src/components/DeptSelect/index.vue
Normal file
211
OrangeFormsOpen-VUE3/src/components/DeptSelect/index.vue
Normal file
@@ -0,0 +1,211 @@
|
||||
<template>
|
||||
<div class="dept-select">
|
||||
<el-select
|
||||
:model-value="value"
|
||||
style="width: 100%"
|
||||
:multiple="multiple"
|
||||
:disabled="disabled"
|
||||
:size="size"
|
||||
:clearable="clearable"
|
||||
:collapse-tags="collapseTags"
|
||||
:placeholder="placeholder"
|
||||
:teleported="false"
|
||||
popper-class="dept-select-popper"
|
||||
@visible-change="onVisibleChange"
|
||||
@remove-tag="onRemoveTag"
|
||||
@clear="onClear"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in selectedItems"
|
||||
:key="item[pps.value]"
|
||||
:label="item[pps.label]"
|
||||
:value="item[pps.value]"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getUUID } from '@/common/utils';
|
||||
import { ANY_OBJECT } from '@/types/generic';
|
||||
import { SysCommonBizController } from '@/api/system';
|
||||
import { Dialog } from '@/components/Dialog';
|
||||
import DeptSelectDlg from './DeptSelectDlg.vue';
|
||||
|
||||
const emit = defineEmits<{ input: [ANY_OBJECT]; change: [ANY_OBJECT] }>();
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
value: string | number | Array<ANY_OBJECT>;
|
||||
size?: '' | 'default' | 'small' | 'large';
|
||||
placeholder?: string;
|
||||
props?: ANY_OBJECT;
|
||||
multiple?: boolean;
|
||||
disabled?: boolean;
|
||||
clearable?: boolean;
|
||||
collapseTags?: boolean;
|
||||
}>(),
|
||||
{
|
||||
props: () => {
|
||||
return {
|
||||
label: 'deptName',
|
||||
value: 'deptId',
|
||||
};
|
||||
},
|
||||
multiple: false,
|
||||
disabled: false,
|
||||
clearable: false,
|
||||
collapseTags: true,
|
||||
},
|
||||
);
|
||||
|
||||
const widgetId = ref(getUUID());
|
||||
const selectedItems = ref<ANY_OBJECT[]>([]);
|
||||
const pps = computed(() => props.props);
|
||||
|
||||
const refreshData = (data: ANY_OBJECT) => {
|
||||
if (data.path === 'thirdSelectDept/' + widgetId.value && data.isSuccess) {
|
||||
handlerEditOperate(data.data);
|
||||
}
|
||||
};
|
||||
const handlerEditOperate = (items: Ref<ANY_OBJECT>) => {
|
||||
console.log('DeptSelect > handlerEditOperate', items);
|
||||
selectedItems.value = [];
|
||||
if (props.multiple) {
|
||||
if (Array.isArray(items.value)) selectedItems.value = items.value;
|
||||
} else {
|
||||
if (items.value != null) selectedItems.value.push(items.value);
|
||||
}
|
||||
if (!checkSelectChange()) return;
|
||||
emitChange();
|
||||
};
|
||||
const onVisibleChange = (visible: boolean) => {
|
||||
if (visible) {
|
||||
Dialog.show<Ref<ANY_OBJECT>>(
|
||||
'部门选择',
|
||||
DeptSelectDlg,
|
||||
{
|
||||
area: ['900px', '650px'],
|
||||
offset: '100px',
|
||||
},
|
||||
{
|
||||
value: selectedItems.value,
|
||||
props: props.props,
|
||||
path: 'thirdSelectDept/' + widgetId.value,
|
||||
multiple: props.multiple,
|
||||
},
|
||||
{
|
||||
width: '900px',
|
||||
height: '650px',
|
||||
pathName: '/thirdParty/thirdSelectDept',
|
||||
},
|
||||
).then(res => {
|
||||
handlerEditOperate(res);
|
||||
});
|
||||
}
|
||||
};
|
||||
const onRemoveTag = (val: ANY_OBJECT) => {
|
||||
selectedItems.value = selectedItems.value.filter(item => {
|
||||
return item[props.props.value] !== val;
|
||||
});
|
||||
emitChange();
|
||||
};
|
||||
const onClear = () => {
|
||||
selectedItems.value = [];
|
||||
emitChange();
|
||||
};
|
||||
const emitChange = () => {
|
||||
let tempValue;
|
||||
if (props.multiple) {
|
||||
tempValue = selectedItems.value.map(item => {
|
||||
return item[props.props.value];
|
||||
});
|
||||
} else {
|
||||
tempValue = (selectedItems.value[0] || {})[props.props.value];
|
||||
}
|
||||
emit('input', tempValue);
|
||||
emit('change', tempValue);
|
||||
};
|
||||
const checkSelectChange = () => {
|
||||
let valueIdString =
|
||||
props.multiple && Array.isArray(props.value)
|
||||
? (props.value || [])
|
||||
.sort((val1: ANY_OBJECT, val2: ANY_OBJECT) => {
|
||||
if (val1 === val2) return 0;
|
||||
return val1 < val2 ? -1 : 1;
|
||||
})
|
||||
.join(',')
|
||||
: props.value || '';
|
||||
let selectedItemsString = selectedItems.value
|
||||
.sort((item1, item2) => {
|
||||
if (item1[props.props.value] === item2[props.props.value]) return 0;
|
||||
return item1[props.props.value] < item2[props.props.value] ? -1 : 1;
|
||||
})
|
||||
.map(item => item[props.props.value])
|
||||
.join(',');
|
||||
return valueIdString !== selectedItemsString;
|
||||
};
|
||||
const getSelectDeptList = () => {
|
||||
let params: ANY_OBJECT = {
|
||||
widgetType: 'upms_dept',
|
||||
};
|
||||
if (props.value == null || props.value === '' || props.value.length <= 0)
|
||||
selectedItems.value = [];
|
||||
if (props.multiple) {
|
||||
params.fieldValues = Array.isArray(props.value) ? props.value : [];
|
||||
} else {
|
||||
params.fieldValues = Array.isArray(props.value) ? props.value[0] : props.value;
|
||||
params.fieldValues = params.fieldValues == null ? [] : [params.fieldValues];
|
||||
}
|
||||
if (Array.isArray(params.fieldValues) && params.fieldValues.length > 0) {
|
||||
params.fieldValues = params.fieldValues.join(',');
|
||||
SysCommonBizController.viewByIds(params, {
|
||||
showMask: false,
|
||||
})
|
||||
.then(res => {
|
||||
if (Array.isArray(res.data)) {
|
||||
selectedItems.value = res.data;
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
console.warn(e);
|
||||
});
|
||||
}
|
||||
};
|
||||
watch(
|
||||
() => props.value,
|
||||
() => {
|
||||
if (props.value) getSelectDeptList();
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
},
|
||||
);
|
||||
|
||||
defineExpose({
|
||||
refreshData,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dept-select :deep(.dept-select-popper) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dept-select :deep(.el-dialog__header) {
|
||||
height: 42px;
|
||||
line-height: 42px;
|
||||
padding: 0 20px;
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
.dept-select :deep(.el-dialog__title) {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
}
|
||||
.dept-select :deep(.el-dialog__headerbtn) {
|
||||
top: 12px;
|
||||
}
|
||||
.dept-select :deep(.el-dialog__body) {
|
||||
padding: 25px;
|
||||
}
|
||||
</style>
|
||||
164
OrangeFormsOpen-VUE3/src/components/Dialog/index.ts
Normal file
164
OrangeFormsOpen-VUE3/src/components/Dialog/index.ts
Normal file
@@ -0,0 +1,164 @@
|
||||
import { layer } from '@layui/layui-vue';
|
||||
import { Component } from 'vue';
|
||||
import { ANY_OBJECT } from '@/types/generic';
|
||||
import { getAppId, getToken, getUUID } from '@/common/utils';
|
||||
import { DialogProp } from './types';
|
||||
import Layout from './layout.vue';
|
||||
|
||||
const LAYER_Z_INDEX = 500;
|
||||
|
||||
export class Dialog {
|
||||
private static index = 0;
|
||||
|
||||
static closeAll = () => {
|
||||
layer.closeAll();
|
||||
Dialog.index = 0;
|
||||
};
|
||||
|
||||
// 未提供单独关闭某个对话框的方法,涉及到z-index的变化规则,若需提供,须考虑z-index的变化规则
|
||||
// options可参考:http://www.layui-vue.com/zh-CN/components/layer 和 https://layui.dev/docs/2/layer/#options
|
||||
|
||||
/**
|
||||
* 打开弹窗
|
||||
* @param {*} title 弹窗标题
|
||||
* @param {*} component 弹窗内容的组件
|
||||
* @param {*} options 弹窗设置(详情请见layui官网 http://www.layui-vue.com/zh-CN/components/layer 和 https://layui.dev/docs/2/layer/#options)
|
||||
* @param {*} params 弹窗组件参数
|
||||
* @param {*} thirdPartyOptions 第三方接入参数
|
||||
* @param {*} thirdPartyOptions.pathName 接入路由name
|
||||
* @param {*} thirdPartyOptions.width 弹窗宽度
|
||||
* @param {*} thirdPartyOptions.height 弹窗高度
|
||||
*/
|
||||
static show = <D>(
|
||||
title: string,
|
||||
component: Component | string,
|
||||
options?: ANY_OBJECT,
|
||||
params?: ANY_OBJECT,
|
||||
thirdPartyOptions?: ANY_OBJECT,
|
||||
) => {
|
||||
// 调用第三方弹窗方法
|
||||
if (getAppId() != null && getAppId() !== '') {
|
||||
if (thirdPartyOptions && window.parent) {
|
||||
showDialog(title, params, thirdPartyOptions);
|
||||
return new Promise<D>((resolve, reject) => {
|
||||
const eventListener = (e: ANY_OBJECT) => {
|
||||
if (e.data.type === 'refreshData') {
|
||||
console.log('第三方弹窗关闭后,回传的数据', e);
|
||||
window.removeEventListener('message', eventListener);
|
||||
resolve(e.data.data?.data as D);
|
||||
}
|
||||
};
|
||||
window.addEventListener('message', eventListener, false);
|
||||
});
|
||||
} else {
|
||||
console.warn('错误的第三方调用!');
|
||||
return Promise.reject('错误的第三方调用!');
|
||||
}
|
||||
}
|
||||
|
||||
return new Promise<D>((resolve, reject) => {
|
||||
const observer: DialogProp<D> = {
|
||||
index: '',
|
||||
cancel: () => {
|
||||
layer.close(observer.index);
|
||||
reject({ message: 'canceled' });
|
||||
},
|
||||
submit: (data: D) => {
|
||||
//console.log('dialog index', observer.index);
|
||||
layer.close(observer.index);
|
||||
resolve(data);
|
||||
},
|
||||
};
|
||||
|
||||
let layerOptions = {
|
||||
title: title,
|
||||
type: 1,
|
||||
skin:
|
||||
'layer-dialog ' + (window.innerWidth <= 1900 ? 'container-default' : 'container-large'),
|
||||
resize: false,
|
||||
offset: 'auto',
|
||||
shadeClose: false,
|
||||
content: '' as string | Component,
|
||||
zIndex: LAYER_Z_INDEX + Dialog.index,
|
||||
end: () => {
|
||||
//console.log('layer end');
|
||||
Dialog.index--;
|
||||
},
|
||||
};
|
||||
// end之后,要执行index--
|
||||
if (options && options.end) {
|
||||
const end = options.end;
|
||||
layerOptions.end = () => {
|
||||
Dialog.index--;
|
||||
if (typeof end == 'function') {
|
||||
end();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
layerOptions = { ...layerOptions, ...options };
|
||||
|
||||
params = { ...params };
|
||||
params.dialog = observer;
|
||||
|
||||
console.log('dialog params', params);
|
||||
//layerOptions.content = h(component, params);
|
||||
layerOptions.content = h(Layout, () => h(component, params));
|
||||
|
||||
const id = layer.open(layerOptions);
|
||||
observer.index = id;
|
||||
Dialog.index++;
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function showDialog(title: string, params?: ANY_OBJECT, options?: ANY_OBJECT) {
|
||||
console.log('第三方弹窗', title, params, options);
|
||||
// 调用第三方弹窗方法
|
||||
if (options && window.parent) {
|
||||
const paramsCopy: ANY_OBJECT = {};
|
||||
if (params) {
|
||||
for (const key in params) {
|
||||
if (Object.prototype.hasOwnProperty.call(params, key)) {
|
||||
const element = params[key];
|
||||
paramsCopy[key] = unref(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const dialogKey = getUUID();
|
||||
const location = window.location;
|
||||
let dlgUrl =
|
||||
location.origin +
|
||||
location.pathname +
|
||||
'#' +
|
||||
options.pathName +
|
||||
'?appId=' +
|
||||
getAppId() +
|
||||
'&token=' +
|
||||
getToken() +
|
||||
'&dlgFullScreen=' +
|
||||
(options.fullscreen ? '1' : '0') +
|
||||
'&dialogKey=' +
|
||||
dialogKey;
|
||||
|
||||
dlgUrl += '&thirdParamsString=' + encodeURIComponent(JSON.stringify(paramsCopy));
|
||||
|
||||
const data = {
|
||||
title: title,
|
||||
dlgFullScreen: options.fullscreen,
|
||||
width: options.width,
|
||||
height: options.height,
|
||||
top: options.top,
|
||||
params: paramsCopy,
|
||||
url: dlgUrl,
|
||||
dialogKey: dialogKey,
|
||||
};
|
||||
|
||||
const dlgOption = {
|
||||
type: 'openDialog',
|
||||
data: JSON.parse(JSON.stringify(data)),
|
||||
};
|
||||
window.parent.postMessage(dlgOption, '*');
|
||||
}
|
||||
}
|
||||
7
OrangeFormsOpen-VUE3/src/components/Dialog/layout.vue
Normal file
7
OrangeFormsOpen-VUE3/src/components/Dialog/layout.vue
Normal file
@@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<el-config-provider :locale="zhCn"><slot /></el-config-provider>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import zhCn from 'element-plus/dist/locale/zh-cn.mjs';
|
||||
</script>
|
||||
5
OrangeFormsOpen-VUE3/src/components/Dialog/types.d.ts
vendored
Normal file
5
OrangeFormsOpen-VUE3/src/components/Dialog/types.d.ts
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
export interface DialogProp<T> {
|
||||
index: string;
|
||||
cancel: () => void;
|
||||
submit: (data: T) => void;
|
||||
}
|
||||
28
OrangeFormsOpen-VUE3/src/components/Dialog/useDialog.ts
Normal file
28
OrangeFormsOpen-VUE3/src/components/Dialog/useDialog.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { EpPropMergeType } from 'element-plus/es/utils/vue/props';
|
||||
import { Dialog } from '@/components/Dialog';
|
||||
import { ANY_OBJECT } from '@/types/generic';
|
||||
|
||||
export const useDialog = () => {
|
||||
const defaultFormItemSize = inject<
|
||||
EpPropMergeType<StringConstructor, '' | 'default' | 'small' | 'large', never> | undefined
|
||||
>('defaultFormItemSize', 'default');
|
||||
|
||||
const show = <D>(
|
||||
title: string,
|
||||
component: Component | string,
|
||||
options?: ANY_OBJECT,
|
||||
params?: ANY_OBJECT,
|
||||
thirdPartyOptions?: ANY_OBJECT,
|
||||
) => {
|
||||
if (!params) {
|
||||
params = {};
|
||||
}
|
||||
params.defaultFormItemSize = defaultFormItemSize;
|
||||
|
||||
return Dialog.show<D>(title, component, options, params, thirdPartyOptions);
|
||||
};
|
||||
|
||||
return {
|
||||
show,
|
||||
};
|
||||
};
|
||||
77
OrangeFormsOpen-VUE3/src/components/FilterBox/index.vue
Normal file
77
OrangeFormsOpen-VUE3/src/components/FilterBox/index.vue
Normal file
@@ -0,0 +1,77 @@
|
||||
<template>
|
||||
<el-row class="flex-box" type="flex">
|
||||
<slot />
|
||||
<div class="search-box" v-if="hasSearch" :style="{ 'min-width': minMenuWidth + 'px' }">
|
||||
<el-button class="search-btn" type="default" plain @click="search" :icon="ElIconSearch"
|
||||
>查询</el-button
|
||||
>
|
||||
<el-button v-if="hasReset" type="default" plain @click="reset" style="width: 72px"
|
||||
>重置</el-button
|
||||
>
|
||||
<div style="float: right">
|
||||
<slot name="operation" />
|
||||
</div>
|
||||
</div>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Search as ElIconSearch } from '@element-plus/icons-vue';
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'reset'): void;
|
||||
(e: 'search'): void;
|
||||
}>();
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
hasSearch?: boolean;
|
||||
hasReset?: boolean;
|
||||
minMenuWidth?: number;
|
||||
}>(),
|
||||
{
|
||||
hasSearch: true,
|
||||
hasReset: true,
|
||||
// 这个值在size为default时会导致某些页面按钮折行
|
||||
minMenuWidth: 300,
|
||||
},
|
||||
);
|
||||
|
||||
const search = () => {
|
||||
emit('search');
|
||||
};
|
||||
const reset = () => {
|
||||
emit('reset');
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.flex-box {
|
||||
padding: 16px 24px 0;
|
||||
margin-bottom: 16px;
|
||||
background-color: white;
|
||||
.search-btn {
|
||||
color: $color-primary;
|
||||
border-color: $color-primary;
|
||||
&:hover {
|
||||
background-color: $color-primary-light-9;
|
||||
}
|
||||
}
|
||||
:deep(.el-form-item) {
|
||||
margin-right: 8px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.extend-box {
|
||||
img {
|
||||
cursor: pointer;
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
.search-box {
|
||||
flex-shrink: 0;
|
||||
padding-left: 8px;
|
||||
margin-bottom: 16px;
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
280
OrangeFormsOpen-VUE3/src/components/IconSelect/icon.json
Normal file
280
OrangeFormsOpen-VUE3/src/components/IconSelect/icon.json
Normal file
@@ -0,0 +1,280 @@
|
||||
[
|
||||
"el-icon-delete-solid",
|
||||
"el-icon-delete",
|
||||
"el-icon-s-tools",
|
||||
"el-icon-setting",
|
||||
"el-icon-user-solid",
|
||||
"el-icon-user",
|
||||
"el-icon-phone",
|
||||
"el-icon-phone-outline",
|
||||
"el-icon-more",
|
||||
"el-icon-more-outline",
|
||||
"el-icon-star-on",
|
||||
"el-icon-star-off",
|
||||
"el-icon-s-goods",
|
||||
"el-icon-goods",
|
||||
"el-icon-warning",
|
||||
"el-icon-warning-outline",
|
||||
"el-icon-question",
|
||||
"el-icon-info",
|
||||
"el-icon-remove",
|
||||
"el-icon-circle-plus",
|
||||
"el-icon-success",
|
||||
"el-icon-error",
|
||||
"el-icon-zoom-in",
|
||||
"el-icon-zoom-out",
|
||||
"el-icon-remove-outline",
|
||||
"el-icon-circle-plus-outline",
|
||||
"el-icon-circle-check",
|
||||
"el-icon-circle-close",
|
||||
"el-icon-s-help",
|
||||
"el-icon-help",
|
||||
"el-icon-minus",
|
||||
"el-icon-plus",
|
||||
"el-icon-check",
|
||||
"el-icon-close",
|
||||
"el-icon-picture",
|
||||
"el-icon-picture-outline",
|
||||
"el-icon-picture-outline-round",
|
||||
"el-icon-upload",
|
||||
"el-icon-upload2",
|
||||
"el-icon-download",
|
||||
"el-icon-camera-solid",
|
||||
"el-icon-camera",
|
||||
"el-icon-video-camera-solid",
|
||||
"el-icon-video-camera",
|
||||
"el-icon-message-solid",
|
||||
"el-icon-bell",
|
||||
"el-icon-s-cooperation",
|
||||
"el-icon-s-order",
|
||||
"el-icon-s-platform",
|
||||
"el-icon-s-fold",
|
||||
"el-icon-s-unfold",
|
||||
"el-icon-s-operation",
|
||||
"el-icon-s-promotion",
|
||||
"el-icon-s-home",
|
||||
"el-icon-s-release",
|
||||
"el-icon-s-ticket",
|
||||
"el-icon-s-management",
|
||||
"el-icon-s-open",
|
||||
"el-icon-s-shop",
|
||||
"el-icon-s-marketing",
|
||||
"el-icon-s-flag",
|
||||
"el-icon-s-comment",
|
||||
"el-icon-s-finance",
|
||||
"el-icon-s-claim",
|
||||
"el-icon-s-custom",
|
||||
"el-icon-s-opportunity",
|
||||
"el-icon-s-data",
|
||||
"el-icon-s-check",
|
||||
"el-icon-s-grid",
|
||||
"el-icon-menu",
|
||||
"el-icon-share",
|
||||
"el-icon-d-caret",
|
||||
"el-icon-caret-left",
|
||||
"el-icon-caret-right",
|
||||
"el-icon-caret-bottom",
|
||||
"el-icon-caret-top",
|
||||
"el-icon-bottom-left",
|
||||
"el-icon-bottom-right",
|
||||
"el-icon-back",
|
||||
"el-icon-right",
|
||||
"el-icon-bottom",
|
||||
"el-icon-top",
|
||||
"el-icon-top-left",
|
||||
"el-icon-top-right",
|
||||
"el-icon-arrow-left",
|
||||
"el-icon-arrow-right",
|
||||
"el-icon-arrow-down",
|
||||
"el-icon-arrow-up",
|
||||
"el-icon-d-arrow-left",
|
||||
"el-icon-d-arrow-right",
|
||||
"el-icon-video-pause",
|
||||
"el-icon-video-play",
|
||||
"el-icon-refresh",
|
||||
"el-icon-refresh-right",
|
||||
"el-icon-refresh-left",
|
||||
"el-icon-finished",
|
||||
"el-icon-sort",
|
||||
"el-icon-sort-up",
|
||||
"el-icon-sort-down",
|
||||
"el-icon-rank",
|
||||
"el-icon-loading",
|
||||
"el-icon-view",
|
||||
"el-icon-c-scale-to-original",
|
||||
"el-icon-date",
|
||||
"el-icon-edit",
|
||||
"el-icon-edit-outline",
|
||||
"el-icon-folder",
|
||||
"el-icon-folder-opened",
|
||||
"el-icon-folder-add",
|
||||
"el-icon-folder-remove",
|
||||
"el-icon-folder-delete",
|
||||
"el-icon-folder-checked",
|
||||
"el-icon-tickets",
|
||||
"el-icon-document-remove",
|
||||
"el-icon-document-delete",
|
||||
"el-icon-document-copy",
|
||||
"el-icon-document-checked",
|
||||
"el-icon-document",
|
||||
"el-icon-document-add",
|
||||
"el-icon-printer",
|
||||
"el-icon-paperclip",
|
||||
"el-icon-takeaway-box",
|
||||
"el-icon-search",
|
||||
"el-icon-monitor",
|
||||
"el-icon-attract",
|
||||
"el-icon-mobile",
|
||||
"el-icon-scissors",
|
||||
"el-icon-umbrella",
|
||||
"el-icon-headset",
|
||||
"el-icon-brush",
|
||||
"el-icon-mouse",
|
||||
"el-icon-coordinate",
|
||||
"el-icon-magic-stick",
|
||||
"el-icon-reading",
|
||||
"el-icon-data-line",
|
||||
"el-icon-data-board",
|
||||
"el-icon-pie-chart",
|
||||
"el-icon-data-analysis",
|
||||
"el-icon-collection-tag",
|
||||
"el-icon-film",
|
||||
"el-icon-suitcase",
|
||||
"el-icon-suitcase-1",
|
||||
"el-icon-receiving",
|
||||
"el-icon-collection",
|
||||
"el-icon-files",
|
||||
"el-icon-notebook-1",
|
||||
"el-icon-notebook-2",
|
||||
"el-icon-toilet-paper",
|
||||
"el-icon-office-building",
|
||||
"el-icon-school",
|
||||
"el-icon-table-lamp",
|
||||
"el-icon-house",
|
||||
"el-icon-no-smoking",
|
||||
"el-icon-smoking",
|
||||
"el-icon-shopping-cart-full",
|
||||
"el-icon-shopping-cart-1",
|
||||
"el-icon-shopping-cart-2",
|
||||
"el-icon-shopping-bag-1",
|
||||
"el-icon-shopping-bag-2",
|
||||
"el-icon-sold-out",
|
||||
"el-icon-sell",
|
||||
"el-icon-present",
|
||||
"el-icon-box",
|
||||
"el-icon-bank-card",
|
||||
"el-icon-money",
|
||||
"el-icon-coin",
|
||||
"el-icon-wallet",
|
||||
"el-icon-discount",
|
||||
"el-icon-price-tag",
|
||||
"el-icon-news",
|
||||
"el-icon-guide",
|
||||
"el-icon-male",
|
||||
"el-icon-female",
|
||||
"el-icon-thumb",
|
||||
"el-icon-cpu",
|
||||
"el-icon-link",
|
||||
"el-icon-connection",
|
||||
"el-icon-open",
|
||||
"el-icon-turn-off",
|
||||
"el-icon-set-up",
|
||||
"el-icon-chat-round",
|
||||
"el-icon-chat-line-round",
|
||||
"el-icon-chat-square",
|
||||
"el-icon-chat-dot-round",
|
||||
"el-icon-chat-dot-square",
|
||||
"el-icon-chat-line-square",
|
||||
"el-icon-message",
|
||||
"el-icon-postcard",
|
||||
"el-icon-position",
|
||||
"el-icon-turn-off-microphone",
|
||||
"el-icon-microphone",
|
||||
"el-icon-close-notification",
|
||||
"el-icon-bangzhu",
|
||||
"el-icon-time",
|
||||
"el-icon-odometer",
|
||||
"el-icon-crop",
|
||||
"el-icon-aim",
|
||||
"el-icon-switch-button",
|
||||
"el-icon-full-screen",
|
||||
"el-icon-copy-document",
|
||||
"el-icon-mic",
|
||||
"el-icon-stopwatch",
|
||||
"el-icon-medal-1",
|
||||
"el-icon-medal",
|
||||
"el-icon-trophy",
|
||||
"el-icon-trophy-1",
|
||||
"el-icon-first-aid-kit",
|
||||
"el-icon-discover",
|
||||
"el-icon-place",
|
||||
"el-icon-location",
|
||||
"el-icon-location-outline",
|
||||
"el-icon-location-information",
|
||||
"el-icon-add-location",
|
||||
"el-icon-delete-location",
|
||||
"el-icon-map-location",
|
||||
"el-icon-alarm-clock",
|
||||
"el-icon-timer",
|
||||
"el-icon-watch-1",
|
||||
"el-icon-watch",
|
||||
"el-icon-lock",
|
||||
"el-icon-unlock",
|
||||
"el-icon-key",
|
||||
"el-icon-service",
|
||||
"el-icon-mobile-phone",
|
||||
"el-icon-bicycle",
|
||||
"el-icon-truck",
|
||||
"el-icon-ship",
|
||||
"el-icon-basketball",
|
||||
"el-icon-football",
|
||||
"el-icon-soccer",
|
||||
"el-icon-baseball",
|
||||
"el-icon-wind-power",
|
||||
"el-icon-light-rain",
|
||||
"el-icon-lightning",
|
||||
"el-icon-heavy-rain",
|
||||
"el-icon-sunrise",
|
||||
"el-icon-sunrise-1",
|
||||
"el-icon-sunset",
|
||||
"el-icon-sunny",
|
||||
"el-icon-cloudy",
|
||||
"el-icon-partly-cloudy",
|
||||
"el-icon-cloudy-and-sunny",
|
||||
"el-icon-moon",
|
||||
"el-icon-moon-night",
|
||||
"el-icon-dish",
|
||||
"el-icon-dish-1",
|
||||
"el-icon-food",
|
||||
"el-icon-chicken",
|
||||
"el-icon-fork-spoon",
|
||||
"el-icon-knife-fork",
|
||||
"el-icon-burger",
|
||||
"el-icon-tableware",
|
||||
"el-icon-sugar",
|
||||
"el-icon-dessert",
|
||||
"el-icon-ice-cream",
|
||||
"el-icon-hot-water",
|
||||
"el-icon-water-cup",
|
||||
"el-icon-coffee-cup",
|
||||
"el-icon-cold-drink",
|
||||
"el-icon-goblet",
|
||||
"el-icon-goblet-full",
|
||||
"el-icon-goblet-square",
|
||||
"el-icon-goblet-square-full",
|
||||
"el-icon-refrigerator",
|
||||
"el-icon-grape",
|
||||
"el-icon-watermelon",
|
||||
"el-icon-cherry",
|
||||
"el-icon-apple",
|
||||
"el-icon-pear",
|
||||
"el-icon-orange",
|
||||
"el-icon-coffee",
|
||||
"el-icon-ice-tea",
|
||||
"el-icon-ice-drink",
|
||||
"el-icon-milk-tea",
|
||||
"el-icon-potato-strips",
|
||||
"el-icon-lollipop",
|
||||
"el-icon-ice-cream-square",
|
||||
"el-icon-ice-cream-round"
|
||||
]
|
||||
126
OrangeFormsOpen-VUE3/src/components/IconSelect/index.vue
Normal file
126
OrangeFormsOpen-VUE3/src/components/IconSelect/index.vue
Normal file
@@ -0,0 +1,126 @@
|
||||
<template>
|
||||
<el-popover width="510" v-model:visible="showDropdown" @show="onDropdownShow" trigger="click">
|
||||
<div class="icon-select-dropdown">
|
||||
<el-row type="flex" style="flex-wrap: wrap">
|
||||
<div
|
||||
v-for="icon in getIconList"
|
||||
:key="icon"
|
||||
class="icon-item"
|
||||
:class="{ active: value === icon }"
|
||||
@click="onIconClick(icon)"
|
||||
:title="icon"
|
||||
>
|
||||
<orange-icon :icon="icon" />
|
||||
</div>
|
||||
</el-row>
|
||||
<el-row type="flex" justify="space-between">
|
||||
<el-button link @click="onClearClick" style="margin-left: 10px">清空</el-button>
|
||||
<el-pagination
|
||||
v-model:current-page="currentPage"
|
||||
:page-size="pageSize"
|
||||
layout="prev, pager, next"
|
||||
:total="getIconCount"
|
||||
>
|
||||
</el-pagination>
|
||||
</el-row>
|
||||
</div>
|
||||
<template v-slot:reference>
|
||||
<div
|
||||
class="icon-select-input"
|
||||
:style="{
|
||||
width: height + 'px',
|
||||
height: height + 'px',
|
||||
'line-height': height + 'px',
|
||||
'font-size': height * 0.5 + 'px',
|
||||
}"
|
||||
>
|
||||
<orange-icon v-if="value" :icon="value" />
|
||||
</div>
|
||||
</template>
|
||||
</el-popover>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue';
|
||||
import * as ElementPlusIconsVue from '@element-plus/icons-vue';
|
||||
import OrangeIcon from '../icons/index.vue';
|
||||
//import iconList from './icon.json';
|
||||
|
||||
const iconList: string[] = [];
|
||||
for (const [key] of Object.entries(ElementPlusIconsVue)) {
|
||||
//console.log(key);
|
||||
iconList.push(key);
|
||||
}
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:value', icon?: string): void;
|
||||
}>();
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
value?: string;
|
||||
height?: number;
|
||||
}>(),
|
||||
{
|
||||
value: '',
|
||||
height: 45,
|
||||
},
|
||||
);
|
||||
|
||||
const showDropdown = ref(false);
|
||||
const currentPage = ref(1);
|
||||
const pageSize = ref(32);
|
||||
|
||||
const onIconClick = (icon: string) => {
|
||||
emit('update:value', icon);
|
||||
showDropdown.value = false;
|
||||
};
|
||||
|
||||
const onClearClick = () => {
|
||||
emit('update:value', '');
|
||||
showDropdown.value = false;
|
||||
};
|
||||
|
||||
const onDropdownShow = () => {
|
||||
currentPage.value = 1;
|
||||
let pos = iconList.indexOf(props.value);
|
||||
if (pos >= 0) {
|
||||
currentPage.value += Math.floor(pos / pageSize.value);
|
||||
}
|
||||
};
|
||||
|
||||
const getIconCount = computed(() => {
|
||||
return iconList.length;
|
||||
});
|
||||
|
||||
const getIconList = computed(() => {
|
||||
let beginPos = (currentPage.value - 1) * pageSize.value;
|
||||
let endPos = beginPos + pageSize.value;
|
||||
return iconList.slice(beginPos, endPos);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.icon-select-input {
|
||||
text-align: center;
|
||||
color: #5f6266;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.icon-item {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin: 10px;
|
||||
font-size: 20px;
|
||||
text-align: center;
|
||||
color: #5f6266;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 3px;
|
||||
line-height: 40px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.active {
|
||||
color: #ef5e1c;
|
||||
}
|
||||
</style>
|
||||
236
OrangeFormsOpen-VUE3/src/components/InputNumberRange/index.vue
Normal file
236
OrangeFormsOpen-VUE3/src/components/InputNumberRange/index.vue
Normal file
@@ -0,0 +1,236 @@
|
||||
<template>
|
||||
<div
|
||||
class="el-input el-date-editor el-range-editor el-input__wrapper el-input-number-range"
|
||||
:class="[
|
||||
inputSize ? 'el-range-editor--' + inputSize : '',
|
||||
focused ? 'is-active' : '',
|
||||
{
|
||||
'is-disabled': inputDisabled,
|
||||
'el-input--prefix': prefixIcon,
|
||||
},
|
||||
]"
|
||||
style="height: 100%"
|
||||
@mouseenter="showClose = true"
|
||||
@mouseleave="showClose = false"
|
||||
>
|
||||
<div class="el-input__icon el-range__icon" :class="prefixIcon">
|
||||
<slot name="prepend"></slot>
|
||||
</div>
|
||||
<input
|
||||
autocomplete="off"
|
||||
:placeholder="startPlaceholder"
|
||||
:value="userInput && userInput[0]"
|
||||
:disabled="inputDisabled"
|
||||
:readonly="readonly"
|
||||
:name="name && name[0]"
|
||||
@input="handleStartInput"
|
||||
@change="handleStartChange"
|
||||
@focus="focused = true"
|
||||
@blur="focused = false"
|
||||
class="el-range-input el-input__wrappe"
|
||||
style="flex-grow: 1"
|
||||
/>
|
||||
<slot name="range-separator">
|
||||
<span>{{ rangeSeparator }}</span>
|
||||
</slot>
|
||||
<input
|
||||
autocomplete="off"
|
||||
:placeholder="endPlaceholder"
|
||||
:value="userInput && userInput[1]"
|
||||
:disabled="inputDisabled"
|
||||
:readonly="readonly"
|
||||
:name="name && name[1]"
|
||||
@input="handleEndInput"
|
||||
@change="handleEndChange"
|
||||
@focus="focused = true"
|
||||
@blur="focused = false"
|
||||
class="el-range-input el-input__wrappe"
|
||||
style="flex-grow: 1"
|
||||
/>
|
||||
<el-icon
|
||||
class="el-input__icon el-range__close-icon"
|
||||
style="margin-right: 5px"
|
||||
:style="{ visibility: showClear ? 'visible' : 'hidden' }"
|
||||
@click="handleClickClear"
|
||||
>
|
||||
<CircleClose />
|
||||
</el-icon>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'InputNumberRange',
|
||||
};
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue';
|
||||
import { useFormItem } from 'element-plus';
|
||||
import { CircleClose } from '@element-plus/icons-vue';
|
||||
|
||||
const { form, formItem } = useFormItem();
|
||||
|
||||
function isNumber(val: string | null | undefined) {
|
||||
if (!val) return false;
|
||||
var regPos = /^\d+(\.\d+)?$/; // 非负浮点数
|
||||
var regNeg = /^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$/; // 负浮点数
|
||||
if (regPos.test(val) || regNeg.test(val)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:value': [(number | null)[] | null];
|
||||
change: [(number | null)[] | null];
|
||||
}>();
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
value?: number[];
|
||||
// small / default / large
|
||||
size?: 'small' | 'default' | 'large' | '';
|
||||
//禁用
|
||||
disabled?: boolean;
|
||||
// 完全只读
|
||||
readonly?: boolean;
|
||||
// 自定义头部图标的类名
|
||||
prefixIcon?: string;
|
||||
/**
|
||||
* 范围选择时最小值的占位内容
|
||||
*/
|
||||
startPlaceholder?: string;
|
||||
/**
|
||||
* 范围选择时最大值的占位内容
|
||||
*/
|
||||
endPlaceholder?: string;
|
||||
name?: string;
|
||||
/**
|
||||
* 选择范围时的分隔符
|
||||
*/
|
||||
rangeSeparator?: string;
|
||||
validateEvent?: boolean;
|
||||
clearable?: boolean;
|
||||
}>(),
|
||||
{
|
||||
value: () => [],
|
||||
size: 'default',
|
||||
name: '',
|
||||
rangeSeparator: '-',
|
||||
validateEvent: true,
|
||||
},
|
||||
);
|
||||
|
||||
const focused = ref(false);
|
||||
const userInput = ref<null | (number | null)[]>(props.value);
|
||||
const showClose = ref(false);
|
||||
|
||||
const inputSize = computed(() => {
|
||||
return props.size || formItem?.size;
|
||||
});
|
||||
const inputDisabled = computed(() => {
|
||||
return props.disabled || form?.disabled;
|
||||
});
|
||||
const showClear = computed(() => {
|
||||
let result =
|
||||
props.clearable &&
|
||||
!inputDisabled.value &&
|
||||
!props.readonly &&
|
||||
showClose.value &&
|
||||
userInput.value != null &&
|
||||
userInput.value.length > 0 &&
|
||||
(userInput.value[0] != null || userInput.value[1] != null);
|
||||
return result;
|
||||
});
|
||||
|
||||
const handleStartInput = (event: Event) => {
|
||||
let value = null;
|
||||
if (event.target) {
|
||||
value = parseInt((event.target as HTMLInputElement).value);
|
||||
}
|
||||
if (value) {
|
||||
value = isNumber(value.toString()) ? value : null;
|
||||
}
|
||||
if (userInput.value) {
|
||||
userInput.value = [value, userInput.value[1]];
|
||||
} else {
|
||||
userInput.value = [value, null];
|
||||
}
|
||||
};
|
||||
|
||||
const handleEndInput = (event: Event) => {
|
||||
let value = null;
|
||||
if (event.target) {
|
||||
value = parseInt((event.target as HTMLInputElement).value);
|
||||
}
|
||||
if (value) {
|
||||
value = isNumber(value.toString()) ? value : null;
|
||||
}
|
||||
if (userInput.value) {
|
||||
userInput.value = [userInput.value[0], value];
|
||||
} else {
|
||||
userInput.value = [null, value];
|
||||
}
|
||||
};
|
||||
const handleStartChange = () => {
|
||||
let value = userInput.value && userInput.value[0];
|
||||
if (userInput.value) {
|
||||
userInput.value[0] = value;
|
||||
} else {
|
||||
userInput.value = [value, null];
|
||||
}
|
||||
//event.srcElement.value = value;
|
||||
emitInput(userInput.value);
|
||||
};
|
||||
const handleEndChange = () => {
|
||||
let value = userInput.value && userInput.value[1];
|
||||
if (userInput.value) {
|
||||
userInput.value[1] = value;
|
||||
} else {
|
||||
userInput.value = [null, value];
|
||||
}
|
||||
//event.srcElement.value = value;
|
||||
emitInput(userInput.value);
|
||||
};
|
||||
const handleClickClear = () => {
|
||||
userInput.value = null;
|
||||
emitInput(userInput.value);
|
||||
};
|
||||
const emitInput = (values: (number | null)[] | null) => {
|
||||
emit('update:value', values);
|
||||
emit('change', values);
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.value,
|
||||
val => {
|
||||
//console.log('number range changed', val);
|
||||
userInput.value = val;
|
||||
// 触发校验
|
||||
formItem?.validate('change').catch(e => {
|
||||
console.warn(e);
|
||||
});
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
},
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.el-input__inner {
|
||||
border: 1px solid #a7b2cb;
|
||||
border-radius: 4px;
|
||||
transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
/*
|
||||
.el-input-number-range.is-active .el-range__close-icon {
|
||||
visibility: visible !important;
|
||||
}
|
||||
*/
|
||||
</style>
|
||||
175
OrangeFormsOpen-VUE3/src/components/MultiItemBox/index.vue
Normal file
175
OrangeFormsOpen-VUE3/src/components/MultiItemBox/index.vue
Normal file
@@ -0,0 +1,175 @@
|
||||
<template>
|
||||
<div class="multi-item-box">
|
||||
<!-- 数据列表 -->
|
||||
<VueDraggable
|
||||
draggable=".column-item"
|
||||
v-model="dataList"
|
||||
:group="dragGroup"
|
||||
style="overflow: hidden; height: 100%"
|
||||
:disabled="!supportSort"
|
||||
>
|
||||
<el-alert
|
||||
v-for="item in dataList"
|
||||
:key="item[prop.value]"
|
||||
class="column-item"
|
||||
:type="itemType"
|
||||
:closable="false"
|
||||
>
|
||||
<template v-slot:title>
|
||||
<el-row type="flex" align="middle" justify="space-between" style="width: 100%">
|
||||
<el-link :type="itemType" :disabled="disabled" @click="onEditItem(item)">
|
||||
<slot v-if="hasSlot" :data="item" />
|
||||
<span v-else>{{ item[prop.label] }}</span>
|
||||
</el-link>
|
||||
<div class="right">
|
||||
<slot name="right" :data="item" />
|
||||
<el-icon><Close /></el-icon>
|
||||
</div>
|
||||
</el-row>
|
||||
</template>
|
||||
</el-alert>
|
||||
</VueDraggable>
|
||||
<!-- 添加按钮 -->
|
||||
<el-alert
|
||||
v-show="maxCount == null || maxCount > (data || []).length"
|
||||
class="column-item"
|
||||
:type="addType"
|
||||
:closable="false"
|
||||
>
|
||||
<template v-slot:title>
|
||||
<el-row type="flex" align="middle" justify="space-between" style="width: 100%">
|
||||
<el-link :disabled="disabled" :type="addType" @click="onEditItem(null)">
|
||||
{{ addText }}
|
||||
</el-link>
|
||||
</el-row>
|
||||
</template>
|
||||
</el-alert>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { VueDraggable } from 'vue-draggable-plus';
|
||||
import { getCurrentInstance } from 'vue';
|
||||
import { Close } from '@element-plus/icons-vue';
|
||||
import { EpPropMergeType } from 'element-plus/es/utils';
|
||||
import { ANY_OBJECT } from '@/types/generic';
|
||||
|
||||
const emit = defineEmits<{
|
||||
add: [];
|
||||
edit: [ANY_OBJECT];
|
||||
delete: [ANY_OBJECT];
|
||||
'update:data': [ANY_OBJECT | ANY_OBJECT[]];
|
||||
}>();
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
/**
|
||||
* 添加按钮类型
|
||||
*/
|
||||
addType?:
|
||||
| EpPropMergeType<StringConstructor, 'info' | 'success' | 'warning', unknown>
|
||||
| undefined;
|
||||
data: ANY_OBJECT | ANY_OBJECT[];
|
||||
/**
|
||||
* 数据项类型
|
||||
*/
|
||||
itemType?:
|
||||
| EpPropMergeType<StringConstructor, 'info' | 'success' | 'warning', unknown>
|
||||
| undefined;
|
||||
/**
|
||||
* 添加按钮文本
|
||||
*/
|
||||
addText?: string;
|
||||
disabled?: boolean;
|
||||
/**
|
||||
* 最大项数,当达到最大项数,则不能添加数据
|
||||
*/
|
||||
maxCount?: number;
|
||||
/**
|
||||
* 是否支持拖拽排序,默认为false
|
||||
*/
|
||||
supportSort?: boolean;
|
||||
/**
|
||||
* 是否支持添加
|
||||
*/
|
||||
supportAdd?: boolean;
|
||||
dragGroup?: string;
|
||||
prop?: ANY_OBJECT;
|
||||
}>(),
|
||||
{
|
||||
addType: 'info',
|
||||
itemType: 'success',
|
||||
addText: '请点击添加数据项',
|
||||
disabled: false,
|
||||
supportSort: false,
|
||||
supportAdd: true,
|
||||
dragGroup: 'componentsGroup',
|
||||
prop: () => {
|
||||
return {
|
||||
// 数据显示字段
|
||||
label: 'name',
|
||||
// 数据值字段
|
||||
value: 'id',
|
||||
};
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const hasSlot = computed(() => {
|
||||
return getCurrentInstance()?.slots.default;
|
||||
});
|
||||
const dataList = computed({
|
||||
get: () => {
|
||||
if (props.data == null) return [];
|
||||
return Array.isArray(props.data) ? props.data : [props.data];
|
||||
},
|
||||
set: (val: ANY_OBJECT[]) => {
|
||||
// 更新数据,调用的地方必须使用v-model:data的方式
|
||||
emit('update:data', val);
|
||||
},
|
||||
});
|
||||
|
||||
const onEditItem = (item: ANY_OBJECT | null) => {
|
||||
if (props.disabled) return;
|
||||
if (item == null) {
|
||||
emit('add');
|
||||
} else {
|
||||
emit('edit', item);
|
||||
}
|
||||
};
|
||||
// const onDeleteItem = (item: ANY_OBJECT) => {
|
||||
// if (props.disabled) return;
|
||||
// emit('delete', item);
|
||||
// };
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.multi-item-box {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
min-height: 30px;
|
||||
padding: 5px;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.multi-item-box :deep(.el-alert) {
|
||||
padding: 0;
|
||||
}
|
||||
.multi-item-box :deep(.el-alert__content) {
|
||||
width: 100%;
|
||||
padding: 5px 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
.multi-item-box :deep(.el-alert__content .el-icon-close) {
|
||||
cursor: pointer;
|
||||
flex-shrink: 0;
|
||||
flex-grow: 0;
|
||||
}
|
||||
.column-item + .column-item {
|
||||
margin-top: 5px;
|
||||
}
|
||||
.column-item :deep(.el-alert__title, .column-item .el-link) {
|
||||
font-size: 12px !important;
|
||||
}
|
||||
</style>
|
||||
213
OrangeFormsOpen-VUE3/src/components/MultiItemList/index.vue
Normal file
213
OrangeFormsOpen-VUE3/src/components/MultiItemList/index.vue
Normal file
@@ -0,0 +1,213 @@
|
||||
<template>
|
||||
<el-form-item class="multi-item-list" :class="{ empty: (dataList || []).length <= 0 }">
|
||||
<template v-slot:label>
|
||||
<el-row justify="space-between" align="middle" style="width: 100%">
|
||||
<span>{{ label }}</span>
|
||||
<right-add-btn
|
||||
class="add-btn"
|
||||
:disabled="addDisabled"
|
||||
@click.prevent.stop="onEditItem(null)"
|
||||
>{{ addText ? addText : '添加' }}</right-add-btn
|
||||
>
|
||||
</el-row>
|
||||
</template>
|
||||
<VueDraggable
|
||||
draggable=".list-item"
|
||||
v-model="dataList"
|
||||
:group="dragGroup"
|
||||
style="display: inline-block; overflow: hidden; width: 100%; height: 100%"
|
||||
:disabled="!supportSort"
|
||||
>
|
||||
<el-row
|
||||
class="list-item"
|
||||
v-for="item in dataList"
|
||||
:key="getItemValue(item)"
|
||||
align="middle"
|
||||
justify="space-between"
|
||||
style="width: 100%; flex-wrap: nowrap"
|
||||
>
|
||||
<div class="item" style="width: 100%; padding: 0 15px">
|
||||
<el-row align="middle" justify="space-between" style="min-height: 32px; padding: 6px 0">
|
||||
<el-link
|
||||
style="font-size: 12px; color: #333; line-height: 20px"
|
||||
:disabled="disabled"
|
||||
@click="onEditItem(item)"
|
||||
>
|
||||
<slot v-if="hasSlot" :data="item" />
|
||||
<span v-else>{{ getItemLabel(item) }}</span>
|
||||
</el-link>
|
||||
<div class="right" style="line-height: 20px; font-size: 12px">
|
||||
<slot name="right" :data="item" />
|
||||
</div>
|
||||
</el-row>
|
||||
</div>
|
||||
<el-icon
|
||||
style="margin-left: 8px; color: #333"
|
||||
class="close-btn"
|
||||
:class="{ disabled: getItemDisabled(item) }"
|
||||
@click.stop="onDeleteItem(item)"
|
||||
><Remove
|
||||
/></el-icon>
|
||||
</el-row>
|
||||
</VueDraggable>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { VueDraggable } from 'vue-draggable-plus';
|
||||
import { getCurrentInstance } from 'vue';
|
||||
import { Remove } from '@element-plus/icons-vue';
|
||||
import { ANY_OBJECT } from '@/types/generic';
|
||||
import RightAddBtn from '@/components/Btns/RightAddBtn.vue';
|
||||
|
||||
const emit = defineEmits<{
|
||||
add: [];
|
||||
edit: [ANY_OBJECT];
|
||||
delete: [ANY_OBJECT];
|
||||
'update:data': [ANY_OBJECT | ANY_OBJECT[]];
|
||||
}>();
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
label: string;
|
||||
data?: ANY_OBJECT | ANY_OBJECT[];
|
||||
disabled?: boolean;
|
||||
/**
|
||||
* 最大项数,当达到最大项数,则不能添加数据
|
||||
*/
|
||||
maxCount?: number;
|
||||
/**
|
||||
* 是否支持拖拽排序,默认为false
|
||||
*/
|
||||
supportSort?: boolean;
|
||||
/**
|
||||
* 是否支持添加
|
||||
*/
|
||||
supportAdd?: boolean;
|
||||
dragGroup?: string;
|
||||
prop?: ANY_OBJECT;
|
||||
addText?: string;
|
||||
}>(),
|
||||
{
|
||||
disabled: false,
|
||||
supportSort: false,
|
||||
supportAdd: true,
|
||||
dragGroup: 'componentsGroup',
|
||||
prop: () => {
|
||||
return {
|
||||
// 数据显示字段
|
||||
label: 'name',
|
||||
// 数据值字段
|
||||
value: 'id',
|
||||
// 数据disabled字段
|
||||
disabled: undefined,
|
||||
};
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const slots = useSlots();
|
||||
console.log('slots', slots);
|
||||
|
||||
const addDisabled = computed(() => {
|
||||
console.log(
|
||||
'addDisabled',
|
||||
props.label,
|
||||
props.disabled,
|
||||
!props.supportAdd,
|
||||
props.maxCount,
|
||||
(dataList.value || []).length,
|
||||
);
|
||||
return (
|
||||
props.disabled ||
|
||||
!props.supportAdd ||
|
||||
(props.maxCount && props.maxCount <= (dataList.value || []).length)
|
||||
);
|
||||
});
|
||||
const hasSlot = computed(() => {
|
||||
console.log('getCurrentInstance.slots', getCurrentInstance()?.slots);
|
||||
return getCurrentInstance()?.slots.default;
|
||||
});
|
||||
const dataList = computed({
|
||||
get: () => {
|
||||
console.log('dataList 0>>>', props.data);
|
||||
if (props.data == null) return [];
|
||||
return Array.isArray(props.data) ? props.data : [props.data];
|
||||
},
|
||||
set: (val: ANY_OBJECT[]) => {
|
||||
console.log('MultiItemList data change', val);
|
||||
// 更新数据,调用的地方必须使用v-model:data的方式
|
||||
emit('update:data', val);
|
||||
},
|
||||
});
|
||||
|
||||
const getItemDisabled = (item: ANY_OBJECT) => {
|
||||
let itemDisabled = false;
|
||||
if (item != null && props.prop.disabled != null) {
|
||||
if (typeof props.prop.disabled === 'function') {
|
||||
itemDisabled = props.prop.disabled(item);
|
||||
} else {
|
||||
itemDisabled = item[props.prop.disabled];
|
||||
}
|
||||
}
|
||||
return props.disabled || item == null || itemDisabled;
|
||||
};
|
||||
const onEditItem = (item: ANY_OBJECT | null) => {
|
||||
if (props.disabled) return;
|
||||
if (item == null) {
|
||||
emit('add');
|
||||
} else {
|
||||
emit('edit', item);
|
||||
}
|
||||
};
|
||||
const onDeleteItem = (item: ANY_OBJECT) => {
|
||||
if (getItemDisabled(item)) return;
|
||||
emit('delete', item);
|
||||
};
|
||||
const getItemValue = (item: ANY_OBJECT) => {
|
||||
console.log('getItemValue >>>', props.label, item[props.prop.value], dataList.value);
|
||||
if (typeof props.prop.value === 'function') return props.prop.value(item);
|
||||
return item[props.prop.value];
|
||||
};
|
||||
const getItemLabel = (item: ANY_OBJECT) => {
|
||||
if (typeof props.prop.label === 'function') return props.prop.label(item);
|
||||
return item[props.prop.label];
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.multi-item-list :deep(.el-form-item__label) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.multi-item-list .item {
|
||||
background: white;
|
||||
border: 1px solid #e8e8e8;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.list-item + .list-item {
|
||||
margin-top: 8px;
|
||||
}
|
||||
.multi-item-list.empty :deep(.el-form-item__content) {
|
||||
height: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.add-btn {
|
||||
position: relative;
|
||||
z-index: 9;
|
||||
margin-left: 5px;
|
||||
font-size: 12px;
|
||||
color: $color-primary;
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.close-btn.disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<label class="page-close-box" @click="onCancel()">
|
||||
<img src="@/assets/img/back2.png" alt="" />
|
||||
</label>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
const emit = defineEmits<{ close: [] }>();
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const onCancel = () => {
|
||||
// TODO 刷新父页面
|
||||
router.go(-1);
|
||||
emit('close');
|
||||
};
|
||||
</script>
|
||||
36
OrangeFormsOpen-VUE3/src/components/Progress/index.vue
Normal file
36
OrangeFormsOpen-VUE3/src/components/Progress/index.vue
Normal file
@@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<el-progress v-bind="$attrs" :percentage="getPercentage" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
/**
|
||||
* 组件最小值
|
||||
*/
|
||||
min?: number;
|
||||
/**
|
||||
* 组件最大值
|
||||
*/
|
||||
max?: number;
|
||||
/**
|
||||
* 组件当前值
|
||||
*/
|
||||
value?: number;
|
||||
}>(),
|
||||
{ min: 0, max: 100, value: 0 },
|
||||
);
|
||||
|
||||
const getPercentage = computed(() => {
|
||||
let value: number = Math.min(props.max, Math.max(props.min, props.value));
|
||||
value = value - props.min;
|
||||
if (props.max - props.min === 0) {
|
||||
value = 0;
|
||||
} else {
|
||||
value = (value * 100) / (props.max - props.min);
|
||||
}
|
||||
return value;
|
||||
});
|
||||
</script>
|
||||
171
OrangeFormsOpen-VUE3/src/components/RichEditor/index.vue
Normal file
171
OrangeFormsOpen-VUE3/src/components/RichEditor/index.vue
Normal file
@@ -0,0 +1,171 @@
|
||||
<template>
|
||||
<div style="border: 1px solid #ccc">
|
||||
<Toolbar
|
||||
style="border-bottom: 1px solid #ccc"
|
||||
:editor="editorRef"
|
||||
:defaultConfig="toolbarConfig"
|
||||
:mode="mode"
|
||||
/>
|
||||
<Editor
|
||||
style="overflow-y: hidden"
|
||||
:style="{ height: height }"
|
||||
v-model="valueHtml"
|
||||
:defaultConfig="editorConfig"
|
||||
:mode="mode"
|
||||
@onCreated="handleCreated"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
// TODO 须测试
|
||||
import '@wangeditor/editor/dist/css/style.css'; // 引入 css
|
||||
import { IEditorConfig, IToolbarConfig } from '@wangeditor/editor';
|
||||
const defaultEditorConfig: Partial<IEditorConfig> = {
|
||||
placeholder: '请输入内容...',
|
||||
MENU_CONF: {
|
||||
uploadImage: {
|
||||
// 小于该值就插入 base64 格式(而不上传),默认为 0
|
||||
base64LimitSize: 1024 * 1024, // 5kb
|
||||
},
|
||||
},
|
||||
};
|
||||
const defaultToolbarConfig: Partial<IToolbarConfig> = {
|
||||
toolbarKeys: [
|
||||
'headerSelect',
|
||||
// '|',
|
||||
// 'header1',
|
||||
// 'header2',
|
||||
// 'header3',
|
||||
// 'header4',
|
||||
// 'header5',
|
||||
'bold',
|
||||
'underline',
|
||||
'italic',
|
||||
'through',
|
||||
// 'code',
|
||||
// 'sub',
|
||||
// 'sup',
|
||||
// 'clearStyle',
|
||||
'color',
|
||||
'bgColor',
|
||||
'fontSize',
|
||||
'fontFamily',
|
||||
'indent',
|
||||
'delIndent',
|
||||
// 'justifyLeft',
|
||||
// 'justifyRight',
|
||||
// 'justifyCenter',
|
||||
// 'justifyJustify',
|
||||
'lineHeight',
|
||||
'insertImage',
|
||||
'uploadImage',
|
||||
// 'deleteImage',
|
||||
// 'editImage',
|
||||
// 'viewImageLink',
|
||||
// 'imageWidth30',
|
||||
// 'imageWidth50',
|
||||
// 'imageWidth100',
|
||||
'divider',
|
||||
// 'emotion',
|
||||
'insertLink',
|
||||
'editLink',
|
||||
'unLink',
|
||||
'viewLink',
|
||||
'codeBlock',
|
||||
'blockquote',
|
||||
'todo',
|
||||
'redo',
|
||||
'undo',
|
||||
'fullScreen',
|
||||
// 'enter',
|
||||
// 'bulletedList',
|
||||
// 'numberedList',
|
||||
// 'insertTable',
|
||||
// 'deleteTable',
|
||||
// 'insertTableRow',
|
||||
// 'deleteTableRow',
|
||||
// 'insertTableCol',
|
||||
// 'deleteTableCol',
|
||||
// 'tableHeader',
|
||||
// 'tableFullWidth',
|
||||
// 'insertVideo',
|
||||
// 'uploadVideo',
|
||||
// 'editVideoSize',
|
||||
// 'uploadImage',
|
||||
// 'codeSelectLang',
|
||||
],
|
||||
};
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onBeforeUnmount, shallowRef } from 'vue';
|
||||
import { Editor, Toolbar } from '@wangeditor/editor-for-vue';
|
||||
import { ANY_OBJECT } from '@/types/generic';
|
||||
|
||||
const emit = defineEmits<{ 'update:value': [string | undefined] }>();
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
value?: string;
|
||||
editorConfig?: ANY_OBJECT;
|
||||
toolbarConfig?: ANY_OBJECT;
|
||||
height?: string;
|
||||
}>(),
|
||||
{
|
||||
editorConfig: () => {
|
||||
return defaultEditorConfig;
|
||||
},
|
||||
toolbarConfig: () => {
|
||||
return defaultToolbarConfig;
|
||||
},
|
||||
height: '300px',
|
||||
},
|
||||
);
|
||||
|
||||
// 编辑器实例,必须用 shallowRef
|
||||
const editorRef = shallowRef();
|
||||
const mode = ref('default');
|
||||
|
||||
// 内容 HTML
|
||||
const valueHtml = computed({
|
||||
get() {
|
||||
return props.value;
|
||||
},
|
||||
set(val) {
|
||||
console.log('richeditor value input', val);
|
||||
emit('update:value', val);
|
||||
},
|
||||
});
|
||||
|
||||
// 组件销毁时,也及时销毁编辑器
|
||||
onBeforeUnmount(() => {
|
||||
const editor = editorRef.value;
|
||||
if (editor == null) return;
|
||||
editor.destroy();
|
||||
});
|
||||
|
||||
const handleCreated = (editor: ANY_OBJECT) => {
|
||||
editorRef.value = editor; // 记录 editor 实例,重要!
|
||||
|
||||
console.log('editor', editor);
|
||||
console.log(editor.getAllMenuKeys());
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.value,
|
||||
newValue => {
|
||||
if (editorRef.value) editorRef.value.txt.html(newValue);
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true,
|
||||
},
|
||||
);
|
||||
|
||||
const getHtml = () => {
|
||||
return editorRef.value ? editorRef.value.txt.html() : undefined;
|
||||
};
|
||||
|
||||
defineExpose({ getHtml });
|
||||
</script>
|
||||
@@ -0,0 +1,11 @@
|
||||
/* eslint no-bitwise: "off" */
|
||||
/*
|
||||
v: int value
|
||||
digit: bit len of v
|
||||
flag: true or false
|
||||
*/
|
||||
const bitmap = (v, digit, flag) => {
|
||||
const b = 1 << digit;
|
||||
return flag ? v | b : v ^ b;
|
||||
};
|
||||
export default bitmap;
|
||||
@@ -0,0 +1,39 @@
|
||||
// src: include chars: [0-9], +, -, *, /
|
||||
// // 9+(3-1)*3+10/2 => 9 3 1-3*+ 10 2/+
|
||||
const infix2suffix = src => {
|
||||
const operatorStack = [];
|
||||
const stack = [];
|
||||
for (let i = 0; i < src.length; i += 1) {
|
||||
const c = src.charAt(i);
|
||||
if (c !== ' ') {
|
||||
if (c >= '0' && c <= '9') {
|
||||
stack.push(c);
|
||||
} else if (c === ')') {
|
||||
let c1 = operatorStack.pop();
|
||||
while (c1 !== '(') {
|
||||
stack.push(c1);
|
||||
c1 = operatorStack.pop();
|
||||
}
|
||||
} else {
|
||||
// priority: */ > +-
|
||||
if (operatorStack.length > 0 && (c === '+' || c === '-')) {
|
||||
const last = operatorStack[operatorStack.length - 1];
|
||||
if (last === '*' || last === '/') {
|
||||
while (operatorStack.length > 0) {
|
||||
stack.push(operatorStack.pop());
|
||||
}
|
||||
}
|
||||
}
|
||||
operatorStack.push(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
while (operatorStack.length > 0) {
|
||||
stack.push(operatorStack.pop());
|
||||
}
|
||||
return stack;
|
||||
};
|
||||
|
||||
export default {
|
||||
infix2suffix,
|
||||
};
|
||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 171 KiB |
@@ -0,0 +1,137 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" version="1.1" x="0" y="0" width="262px" height="72px" viewBox="0 0 262 72" preserveAspectRatio="none">
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(0,0)"><path fill="#000000" fill-rule="evenodd" d="M11.5656391,4.43436088 L9,7 L16,7 L16,0 L13.0418424,2.95815758 C11.5936787,1.73635959 9.72260775,1 7.67955083,1 C4.22126258,1 1.25575599,3.10984908 0,6 L2,7 C2.93658775,4.60974406 5.12943697,3.08011229 7.67955083,3 C9.14881247,3.0528747 10.4994783,3.57862053 11.5656391,4.43436088 Z" transform="matrix(-1 0 0 1 17 5)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(18,0)"><path fill="#000000" fill-rule="evenodd" d="M11.5656391,4.43436088 L9,7 L16,7 L16,0 L13.0418424,2.95815758 C11.5936787,1.73635959 9.72260775,1 7.67955083,1 C4.22126258,1 1.25575599,3.10984908 0,6 L2,7 C2.93658775,4.60974406 5.12943697,3.08011229 7.67955083,3 C9.14881247,3.0528747 10.4994783,3.57862053 11.5656391,4.43436088 Z" transform="translate(1 5)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(36,0)"><path fill="#000000" fill-rule="evenodd" d="M13,14 L3,14 L3,11 L0,11 L0,6.00591905 C0,4.89808055 0.894513756,4 1.99406028,4 L14.0059397,4 C15.1072288,4 16,4.88655484 16,6.00591905 L16,11 L13,11 L13,14 Z M5,9 L11,9 L11,12 L5,12 L5,9 Z M3,0 L13,0 L13,3 L3,3 L3,0 Z M12,6 L14,6 L14,8 L12,8 L12,6 Z" transform="translate(1 2)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(54,0)"><path fill="#000000" fill-rule="evenodd" d="M9,0 L1,0 C0.45,0 0,0.45 0,1 L0,4 C0,4.55 0.45,5 1,5 L9,5 C9.55,5 10,4.55 10,4 L10,3 L11,3 L11,6 L4,6 L4,14 L6,14 L6,8 L13,8 L13,2 L10,2 L10,1 C10,0.45 9.55,0 9,0 Z" transform="translate(3 2)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(72,0)"><path fill="#000000" fill-rule="evenodd" d="M0.27,1.55 L5.43,6.7 L3,12 L5.5,12 L7.14,8.42 L11.73,13 L13,11.73 L1.55,0.27 L0.27,1.55 L0.27,1.55 Z M3.82,0 L5.82,2 L7.58,2 L7.03,3.21 L8.74,4.92 L10.08,2 L14,2 L14,0 L3.82,0 L3.82,0 Z" transform="translate(2 3)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(90,0)"><path fill="#000000" fill-rule="evenodd" d="M9,3.5 C9,1.57 7.43,0 5.5,0 L1.77635684e-15,0 L1.77635684e-15,12 L6.25,12 C8.04,12 9.5,10.54 9.5,8.75 C9.5,7.45 8.73,6.34 7.63,5.82 C8.46,5.24 9,4.38 9,3.5 Z M5,2 C5.82999992,2 6.5,2.67 6.5,3.5 C6.5,4.33 5.82999992,5 5,5 L3,5 L3,2 L5,2 Z M3,10 L3,7 L5.5,7 C6.32999992,7 7,7.67 7,8.5 C7,9.33 6.32999992,10 5.5,10 L3,10 Z" transform="translate(4 3)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(108,0)"><polygon fill="#000000" fill-rule="evenodd" points="4 0 4 2 6.58 2 2.92 10 0 10 0 12 8 12 8 10 5.42 10 9.08 2 12 2 12 0" transform="translate(3 3)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(126,0)"><path fill="#000000" d="M6,12 C8.76,12 11,9.76 11,7 L11,0 L9,0 L9,7 C9,8.75029916 7.49912807,10 6,10 C4.50087193,10 3,8.75837486 3,7 L3,0 L1,0 L1,7 C1,9.76 3.24,12 6,12 Z M0,13 L0,15 L12,15 L12,13 L0,13 Z" transform="translate(3 3)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(144,0)"><path fill="#010101" fill-rule="evenodd" d="M2.8875,3.06 C2.8875,2.6025 2.985,2.18625 3.18375,1.8075 C3.3825,1.42875 3.66,1.10625 4.02,0.84 C4.38,0.57375 4.80375,0.3675 5.29875,0.22125 C5.79375,0.075 6.33375,0 6.92625,0 C7.53375,0 8.085,0.0825 8.58,0.25125 C9.075,0.42 9.49875,0.6525 9.85125,0.95625 C10.20375,1.25625 10.47375,1.6125 10.665,2.02875 C10.85625,2.44125 10.95,2.895 10.95,3.38625 L8.6925,3.38625 C8.6925,3.1575 8.655,2.94375 8.58375,2.74875 C8.5125,2.55 8.4,2.38125 8.25,2.2425 C8.1,2.10375 7.9125,1.99125 7.6875,1.91625 C7.4625,1.8375 7.19625,1.8 6.88875,1.8 C6.5925,1.8 6.3375,1.83375 6.11625,1.8975 C5.89875,1.96125 5.71875,2.05125 5.57625,2.1675 C5.43375,2.28375 5.325,2.41875 5.25375,2.5725 C5.1825,2.72625 5.145,2.895 5.145,3.0675 C5.145,3.4275 5.32875,3.73125 5.69625,3.975 C5.71780203,3.98908066 5.73942012,4.00311728 5.76118357,4.01733315 C6.02342923,4.18863185 6.5,4.5 7,5 L4,5 C4,5 3.21375,4.37625 3.17625,4.30875 C2.985,3.9525 2.8875,3.53625 2.8875,3.06 Z M14,6 L0,6 L0,8 L7.21875,8 C7.35375,8.0525 7.51875,8.105 7.63125,8.15375 C7.90875,8.2775 8.12625,8.40875 8.28375,8.53625 C8.44125,8.6675 8.54625,8.81 8.6025,8.96 C8.65875,9.11375 8.685,9.28625 8.685,9.47375 C8.685,9.65 8.65125,9.815 8.58375,9.965 C8.51625,10.11875 8.41125,10.25 8.2725,10.35875 C8.13375,10.4675 7.95375,10.55375 7.74,10.6175 C7.5225,10.68125 7.27125,10.71125 6.97875,10.71125 C6.6525,10.71125 6.35625,10.6775 6.09,10.61375 C5.82375,10.55 5.59875,10.445 5.41125,10.3025 C5.22375,10.16 5.0775,9.9725 4.9725,9.74375 C4.8675,9.515 4.78125,9.17 4.78125,9 L2.55,9 C2.55,9.2525 2.61,9.6875 2.72625,10.025 C2.8425,10.3625 3.0075,10.66625 3.21375,10.9325 C3.42,11.19875 3.6675,11.4275 3.94875,11.6225 C4.23,11.8175 4.53375,11.9825 4.86375,12.11 C5.19375,12.24125 5.535,12.33875 5.89875,12.39875 C6.25875,12.4625 6.6225,12.4925 6.9825,12.4925 C7.5825,12.4925 8.13,12.425 8.6175,12.28625 C9.105,12.1475 9.525,11.94875 9.87,11.69375 C10.215,11.435 10.48125,11.12 10.6725,10.74125 C10.86375,10.3625 10.95375,9.935 10.95375,9.455 C10.95375,9.005 10.875,8.6 10.72125,8.24375 C10.68375,8.1575 10.6425,8.075 10.59375,7.9925 L14,8 L14,6 Z" transform="translate(2 3)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(162,0)"><path fill="#000000" fill-rule="evenodd" d="M7,0 L5,0 L0.5,12 L2.5,12 L3.62,9 L8.37,9 L9.49,12 L11.49,12 L7,0 L7,0 Z M4.38,7 L6,2.67 L7.62,7 L4.38,7 L4.38,7 Z" transform="translate(3 1)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(180,0)"><g fill="none" fill-rule="evenodd">
|
||||
<path fill="#000000" d="M14.5,8.87 C14.5,8.87 13,10.49 13,11.49 C13,12.32 13.67,12.99 14.5,12.99 C15.33,12.99 16,12.32 16,11.49 C16,10.5 14.5,8.87 14.5,8.87 L14.5,8.87 Z M12.71,6.79 L5.91,0 L4.85,1.06 L6.44,2.65 L2.29,6.79 C1.9,7.18 1.9,7.81 2.29,8.2 L6.79,12.7 C6.99,12.9 7.24,13 7.5,13 C7.76,13 8.01,12.9 8.21,12.71 L12.71,8.21 C13.1,7.82 13.1,7.18 12.71,6.79 L12.71,6.79 Z M4.21,7 L7.5,3.71 L10.79,7 L4.21,7 L4.21,7 Z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(198,0)"><path fill="#000000" fill-rule="evenodd" d="M3,6 L1,6 L1,2 L8,2 L8,4 L3,4 L3,6 Z M10,4 L10,2 L17,2 L17,6 L15,6 L15,4 L10,4 Z M10,14 L15,14 L15,12 L17,12 L17,16 L10,16 L10,14 Z M1,12 L3,12 L3,14 L8,14 L8,16 L1,16 L1,12 Z M1,8 L5,8 L5,6 L8,9 L5,12 L5,10 L1,10 L1,8 Z M10,9 L13,6 L13,8 L17,8 L17,10 L13,10 L13,12 L10,9 Z"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(216,0)"><path fill="#000000" fill-rule="evenodd" d="M0,14 L10,14 L10,12 L0,12 L0,14 Z M10,4 L0,4 L0,6 L10,6 L10,4 Z M0,0 L0,2 L14,2 L14,0 L0,0 Z M0,10 L14,10 L14,8 L0,8 L0,10 Z" transform="translate(2 2)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(234,0)"><path fill="#000000" fill-rule="evenodd" d="M2,12 L2,14 L12,14 L12,12 L2,12 Z M2,4 L2,6 L12,6 L12,4 L2,4 Z M0,10 L14,10 L14,8 L0,8 L0,10 Z M0,0 L0,2 L14,2 L14,0 L0,0 Z" transform="translate(2 2)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(0,18)"><path fill="#000000" fill-rule="evenodd" d="M4,14 L14,14 L14,12 L4,12 L4,14 Z M0,10 L14,10 L14,8 L0,8 L0,10 Z M0,0 L0,2 L14,2 L14,0 L0,0 Z M4,6 L14,6 L14,4 L4,4 L4,6 Z" transform="translate(2 2)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(18,18)"><path fill="#000000" fill-rule="evenodd" d="M0,0 L0,2 L12,2 L12,0 L0,0 L0,0 Z M2.5,7 L5,7 L5,14 L7,14 L7,7 L9.5,7 L6,3.5 L2.5,7 L2.5,7 Z" transform="translate(3 2)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(36,18)"><path fill="#000000" fill-rule="evenodd" d="M9.5,3 L7,3 L7,0 L5,0 L5,3 L2.5,3 L6,6.5 L9.5,3 L9.5,3 Z M0,8 L0,10 L12,10 L12,8 L0,8 L0,8 Z M2.5,15 L5,15 L5,18 L7,18 L7,15 L9.5,15 L6,11.5 L2.5,15 L2.5,15 Z" transform="translate(3)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(54,18)"><path fill="#000000" fill-rule="evenodd" d="M9.5,7 L7,7 L7,0 L5,0 L5,7 L2.5,7 L6,10.5 L9.5,7 L9.5,7 Z M0,12 L0,14 L12,14 L12,12 L0,12 L0,12 Z" transform="translate(3 2)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(72,18)"><path fill="#000000" fill-rule="evenodd" d="M14,0 L0,0 L0,2 L14,2 L14,0 Z M0,12 L4,12 L4,10 L0,10 L0,12 Z M11.5,5 L0,5 L0,7 L11.75,7 C12.58,7 13.25,7.67 13.25,8.5 C13.25,9.33 12.58,10 11.75,10 L9,10 L9,8 L6,11 L9,14 L9,12 L11.5,12 C13.43,12 15,10.43 15,8.5 C15,6.57 13.43,5 11.5,5 Z" transform="translate(2 3)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(90,18)"><path fill="#000000" fill-rule="evenodd" d="M0,0 L0,1 L6,7 L6,12 L8,11 L8,7 L14,1 L14,0 L0,0 Z M4,3 L10,3 L7,6 L4,3 Z" transform="translate(2 3)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(108,18)"><polygon fill="#000000" fill-rule="evenodd" points="10 0 0 0 0 1.8 5.5 7 0 12.2 0 14 10 14 10 12 3.1 12 8 7 3.1 2 10 2" transform="translate(4 2)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(126,18)"><polygon fill="#000000" fill-rule="evenodd" points="0 0 4 4 8 0" transform="translate(5 7)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(144,18)"><polygon fill="#000000" fill-rule="evenodd" points="-2 2 2 6 6 2" transform="rotate(-90 8 3)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(162,18)"><path fill="#000000" fill-rule="evenodd" d="M1.9,4 C1.9,2.84 2.84,1.9 4,1.9 L8,1.9 L8,0 L4,0 C1.79,0 0,1.79 0,4 C0,6.21 1.79,8 4,8 L8,8 L8,6.1 L4,6.1 C2.84,6.1 1.9,5.16 1.9,4 L1.9,4 Z M14,0 L10,0 L10,1.9 L14,1.9 C15.16,1.9 16.1,2.84 16.1,4 C16.1,5.16 15.16,6.1 14,6.1 L10,6.1 L10,8 L14,8 C16.21,8 18,6.21 18,4 C18,1.79 16.21,0 14,0 L14,0 Z M6,5 L12,5 L12,3 L6,3 L6,5 L6,5 Z" transform="translate(0 5)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(180,18)"><path fill="#000000" fill-rule="evenodd" d="M15,0 C15.55,0 16,0.45 16,1 L16,15 C16,15.55 15.55,16 15,16 L1,16 C0.45,16 0,15.55 0,15 L0,1 C0,0.45 0.45,0 1,0 L15,0 Z M2,2 L2,14 L14,14 L14,2 L2,2 Z M6,12 L4,12 L4,7 L6,7 L6,12 L6,12 Z M9,12 L7,12 L7,4 L9,4 L9,12 L9,12 Z M12,12 L10,12 L10,8 L12,8 L12,12 L12,12 Z" transform="translate(1 1)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(198,18)"><g fill="none" fill-rule="evenodd">
|
||||
<path stroke="#000000" d="M1.5 3.5H16.5V15.5H1.5z"/>
|
||||
<path fill="#000000" d="M6 8H7V15H6z"/>
|
||||
<path fill="#D8D8D8" d="M2 4H16V7H2z"/>
|
||||
<path fill="#000000" d="M2 7H16V8H2zM2 11H16V12H2z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(216,18)"><path fill="#000000" fill-rule="evenodd" d="M2,0.5 C1.17,0.5 0.5,1.17 0.5,2 C0.5,2.83 1.17,3.5 2,3.5 C2.83,3.5 3.5,2.83 3.5,2 C3.5,1.17 2.83,0.5 2,0.5 L2,0.5 Z M12,0.5 C11.17,0.5 10.5,1.17 10.5,2 C10.5,2.83 11.17,3.5 12,3.5 C12.83,3.5 13.5,2.83 13.5,2 C13.5,1.17 12.83,0.5 12,0.5 L12,0.5 Z M7,0.5 C6.17,0.5 5.5,1.17 5.5,2 C5.5,2.83 6.17,3.5 7,3.5 C7.83,3.5 8.5,2.83 8.5,2 C8.5,1.17 7.83,0.5 7,0.5 L7,0.5 Z" transform="translate(2 7)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(234,18)"><path fill="#000000" fill-rule="evenodd" d="M6,4 L6,0 L4,0 L4,4 L0,4 L0,6 L4,6 L4,10 L6,10 L6,6 L10,6 L10,4 L6,4 Z" transform="translate(4 4)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(0,36)"><path fill="#000000" fill-rule="evenodd" d="M0,0 L0,14 L14,14 L14,0 L0,0 L0,0 Z M6,12 L2,12 L2,8 L6,8 L6,12 L6,12 Z M6,6 L2,6 L2,2 L6,2 L6,6 L6,6 Z M12,12 L8,12 L8,8 L12,8 L12,12 L12,12 Z M12,6 L8,6 L8,2 L12,2 L12,6 L12,6 Z" transform="translate(2 2)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(18,36)">
|
||||
<g fill="#000000" fill-rule="evenodd" transform="translate(2 2)">
|
||||
<path d="M0,14 L2,14 L2,12 L0,12 L0,14 L0,14 Z M2,3 L0,3 L0,5 L2,5 L2,3 L2,3 Z M3,14 L5,14 L5,12 L3,12 L3,14 L3,14 Z M11,0 L9,0 L9,2 L11,2 L11,0 L11,0 Z M2,0 L0,0 L0,2 L2,2 L2,0 L2,0 Z M5,0 L3,0 L3,2 L5,2 L5,0 L5,0 Z M0,11 L2,11 L2,9 L0,9 L0,11 L0,11 Z M9,14 L11,14 L11,12 L9,12 L9,14 L9,14 Z M12,0 L12,2 L14,2 L14,0 L12,0 L12,0 Z M12,5 L14,5 L14,3 L12,3 L12,5 L12,5 Z M12,14 L14,14 L14,12 L12,12 L12,14 L12,14 Z M12,11 L14,11 L14,9 L12,9 L12,11 L12,11 Z" opacity=".54"/>
|
||||
<polygon points="8 0 6 0 6 6 0 6 0 8 6 8 6 14 8 14 8 8 14 8 14 6 8 6"/>
|
||||
</g>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(36,36)">
|
||||
<g fill="#000000" fill-rule="evenodd" transform="translate(2 2)">
|
||||
<path d="M6,14 L8,14 L8,12 L6,12 L6,14 L6,14 Z M3,2 L5,2 L5,0 L3,0 L3,2 L3,2 Z M6,11 L8,11 L8,9 L6,9 L6,11 L6,11 Z M3,14 L5,14 L5,12 L3,12 L3,14 L3,14 Z M0,5 L2,5 L2,3 L0,3 L0,5 L0,5 Z M0,14 L2,14 L2,12 L0,12 L0,14 L0,14 Z M0,2 L2,2 L2,0 L0,0 L0,2 L0,2 Z M0,11 L2,11 L2,9 L0,9 L0,11 L0,11 Z M12,11 L14,11 L14,9 L12,9 L12,11 L12,11 Z M12,14 L14,14 L14,12 L12,12 L12,14 L12,14 Z M12,5 L14,5 L14,3 L12,3 L12,5 L12,5 Z M12,0 L12,2 L14,2 L14,0 L12,0 L12,0 Z M6,2 L8,2 L8,0 L6,0 L6,2 L6,2 Z M9,2 L11,2 L11,0 L9,0 L9,2 L9,2 Z M6,5 L8,5 L8,3 L6,3 L6,5 L6,5 Z M9,14 L11,14 L11,12 L9,12 L9,14 L9,14 Z" opacity=".54"/>
|
||||
<polygon points="0 8 14 8 14 6 0 6"/>
|
||||
</g>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(54,36)">
|
||||
<g fill="#000000" fill-rule="evenodd" transform="translate(2 2)">
|
||||
<path d="M3,14 L5,14 L5,12 L3,12 L3,14 L3,14 Z M0,5 L2,5 L2,3 L0,3 L0,5 L0,5 Z M0,2 L2,2 L2,0 L0,0 L0,2 L0,2 Z M3,8 L5,8 L5,6 L3,6 L3,8 L3,8 Z M3,2 L5,2 L5,0 L3,0 L3,2 L3,2 Z M0,14 L2,14 L2,12 L0,12 L0,14 L0,14 Z M0,8 L2,8 L2,6 L0,6 L0,8 L0,8 Z M0,11 L2,11 L2,9 L0,9 L0,11 L0,11 Z M12,0 L12,2 L14,2 L14,0 L12,0 L12,0 Z M12,8 L14,8 L14,6 L12,6 L12,8 L12,8 Z M12,14 L14,14 L14,12 L12,12 L12,14 L12,14 Z M12,5 L14,5 L14,3 L12,3 L12,5 L12,5 Z M12,11 L14,11 L14,9 L12,9 L12,11 L12,11 Z M9,14 L11,14 L11,12 L9,12 L9,14 L9,14 Z M9,8 L11,8 L11,6 L9,6 L9,8 L9,8 Z M9,2 L11,2 L11,0 L9,0 L9,2 L9,2 Z" opacity=".54"/>
|
||||
<polygon points="6 14 8 14 8 0 6 0"/>
|
||||
</g>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(72,36)">
|
||||
<g fill="#000000" fill-rule="evenodd" transform="translate(2 2)">
|
||||
<path d="M8,3 L6,3 L6,5 L8,5 L8,3 L8,3 Z M11,6 L9,6 L9,8 L11,8 L11,6 L11,6 Z M8,6 L6,6 L6,8 L8,8 L8,6 L8,6 Z M8,9 L6,9 L6,11 L8,11 L8,9 L8,9 Z M5,6 L3,6 L3,8 L5,8 L5,6 L5,6 Z" opacity=".54"/>
|
||||
<path d="M0,0 L14,0 L14,14 L0,14 L0,0 Z M12,12 L12,2 L2,2 L2,12 L12,12 Z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(90,36)">
|
||||
<g fill="#000000" fill-rule="evenodd" transform="translate(2 2)">
|
||||
<path d="M6,8 L8,8 L8,6 L6,6 L6,8 L6,8 Z M6,5 L8,5 L8,3 L6,3 L6,5 L6,5 Z M6,11 L8,11 L8,9 L6,9 L6,11 L6,11 Z M6,14 L8,14 L8,12 L6,12 L6,14 L6,14 Z M3,14 L5,14 L5,12 L3,12 L3,14 L3,14 Z M3,2 L5,2 L5,0 L3,0 L3,2 L3,2 Z M3,8 L5,8 L5,6 L3,6 L3,8 L3,8 Z M12,14 L14,14 L14,12 L12,12 L12,14 L12,14 Z M12,8 L14,8 L14,6 L12,6 L12,8 L12,8 Z M12,11 L14,11 L14,9 L12,9 L12,11 L12,11 Z M12,5 L14,5 L14,3 L12,3 L12,5 L12,5 Z M6,2 L8,2 L8,0 L6,0 L6,2 L6,2 Z M12,0 L12,2 L14,2 L14,0 L12,0 L12,0 Z M9,14 L11,14 L11,12 L9,12 L9,14 L9,14 Z M9,8 L11,8 L11,6 L9,6 L9,8 L9,8 Z M9,2 L11,2 L11,0 L9,0 L9,2 L9,2 Z" opacity=".54"/>
|
||||
<polygon points="0 14 2 14 2 0 0 0"/>
|
||||
</g>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(108,36)">
|
||||
<g fill="#000000" fill-rule="evenodd" transform="translate(2 2)">
|
||||
<path d="M3,8 L5,8 L5,6 L3,6 L3,8 L3,8 Z M0,14 L2,14 L2,12 L0,12 L0,14 L0,14 Z M6,14 L8,14 L8,12 L6,12 L6,14 L6,14 Z M6,11 L8,11 L8,9 L6,9 L6,11 L6,11 Z M3,14 L5,14 L5,12 L3,12 L3,14 L3,14 Z M0,11 L2,11 L2,9 L0,9 L0,11 L0,11 Z M6,8 L8,8 L8,6 L6,6 L6,8 L6,8 Z M0,5 L2,5 L2,3 L0,3 L0,5 L0,5 Z M0,8 L2,8 L2,6 L0,6 L0,8 L0,8 Z M12,8 L14,8 L14,6 L12,6 L12,8 L12,8 Z M12,11 L14,11 L14,9 L12,9 L12,11 L12,11 Z M12,5 L14,5 L14,3 L12,3 L12,5 L12,5 Z M6,5 L8,5 L8,3 L6,3 L6,5 L6,5 Z M9,14 L11,14 L11,12 L9,12 L9,14 L9,14 Z M9,8 L11,8 L11,6 L9,6 L9,8 L9,8 Z M12,14 L14,14 L14,12 L12,12 L12,14 L12,14 Z" opacity=".54"/>
|
||||
<polygon points="0 0 0 2 14 2 14 0"/>
|
||||
</g>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(126,36)">
|
||||
<g fill="#000000" fill-rule="evenodd" transform="translate(2 2)">
|
||||
<path d="M0,2 L2,2 L2,0 L0,0 L0,2 L0,2 Z M3,2 L5,2 L5,0 L3,0 L3,2 L3,2 Z M3,8 L5,8 L5,6 L3,6 L3,8 L3,8 Z M3,14 L5,14 L5,12 L3,12 L3,14 L3,14 Z M0,5 L2,5 L2,3 L0,3 L0,5 L0,5 Z M0,8 L2,8 L2,6 L0,6 L0,8 L0,8 Z M0,14 L2,14 L2,12 L0,12 L0,14 L0,14 Z M0,11 L2,11 L2,9 L0,9 L0,11 L0,11 Z M9,8 L11,8 L11,6 L9,6 L9,8 L9,8 Z M6,14 L8,14 L8,12 L6,12 L6,14 L6,14 Z M9,14 L11,14 L11,12 L9,12 L9,14 L9,14 Z M6,2 L8,2 L8,0 L6,0 L6,2 L6,2 Z M9,2 L11,2 L11,0 L9,0 L9,2 L9,2 Z M6,11 L8,11 L8,9 L6,9 L6,11 L6,11 Z M6,5 L8,5 L8,3 L6,3 L6,5 L6,5 Z M6,8 L8,8 L8,6 L6,6 L6,8 L6,8 Z" opacity=".54"/>
|
||||
<polygon points="12 0 12 14 14 14 14 0"/>
|
||||
</g>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(144,36)">
|
||||
<g fill="#000000" fill-rule="evenodd" transform="translate(2 2)">
|
||||
<path d="M5,0 L3,0 L3,2 L5,2 L5,0 L5,0 Z M8,6 L6,6 L6,8 L8,8 L8,6 L8,6 Z M8,9 L6,9 L6,11 L8,11 L8,9 L8,9 Z M11,6 L9,6 L9,8 L11,8 L11,6 L11,6 Z M5,6 L3,6 L3,8 L5,8 L5,6 L5,6 Z M11,0 L9,0 L9,2 L11,2 L11,0 L11,0 Z M8,3 L6,3 L6,5 L8,5 L8,3 L8,3 Z M8,0 L6,0 L6,2 L8,2 L8,0 L8,0 Z M2,9 L0,9 L0,11 L2,11 L2,9 L2,9 Z M12,11 L14,11 L14,9 L12,9 L12,11 L12,11 Z M12,5 L14,5 L14,3 L12,3 L12,5 L12,5 Z M12,8 L14,8 L14,6 L12,6 L12,8 L12,8 Z M12,0 L12,2 L14,2 L14,0 L12,0 L12,0 Z M2,0 L0,0 L0,2 L2,2 L2,0 L2,0 Z M2,3 L0,3 L0,5 L2,5 L2,3 L2,3 Z M2,6 L0,6 L0,8 L2,8 L2,6 L2,6 Z" opacity=".54"/>
|
||||
<polygon points="0 14 14 14 14 12 0 12"/>
|
||||
</g>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(162,36)"><path fill="#000000" fill-rule="evenodd" d="M6,14 L8,14 L8,12 L6,12 L6,14 L6,14 Z M3,8 L5,8 L5,6 L3,6 L3,8 L3,8 Z M3,2 L5,2 L5,0 L3,0 L3,2 L3,2 Z M6,11 L8,11 L8,9 L6,9 L6,11 L6,11 Z M3,14 L5,14 L5,12 L3,12 L3,14 L3,14 Z M0,5 L2,5 L2,3 L0,3 L0,5 L0,5 Z M0,14 L2,14 L2,12 L0,12 L0,14 L0,14 Z M0,2 L2,2 L2,0 L0,0 L0,2 L0,2 Z M0,8 L2,8 L2,6 L0,6 L0,8 L0,8 Z M6,8 L8,8 L8,6 L6,6 L6,8 L6,8 Z M0,11 L2,11 L2,9 L0,9 L0,11 L0,11 Z M12,11 L14,11 L14,9 L12,9 L12,11 L12,11 Z M12,14 L14,14 L14,12 L12,12 L12,14 L12,14 Z M12,8 L14,8 L14,6 L12,6 L12,8 L12,8 Z M12,5 L14,5 L14,3 L12,3 L12,5 L12,5 Z M12,0 L12,2 L14,2 L14,0 L12,0 L12,0 Z M6,2 L8,2 L8,0 L6,0 L6,2 L6,2 Z M9,2 L11,2 L11,0 L9,0 L9,2 L9,2 Z M6,5 L8,5 L8,3 L6,3 L6,5 L6,5 Z M9,14 L11,14 L11,12 L9,12 L9,14 L9,14 Z M9,8 L11,8 L11,6 L9,6 L9,8 L9,8 Z" transform="translate(2 2)" opacity=".54"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(180,36)"><path fill="#000000" fill-rule="evenodd" d="M6.5,3.62 L0,10.12 L0,13 L2.88,13 L9.38,6.5 L6.5,3.62 Z M11.85,4.02 C12.05,3.82 12.05,3.51 11.85,3.31 L9.68,1.14 C9.48,0.94 9.17,0.94 8.97,1.14 L7.62,2.5 L10.5,5.38 L11.85,4.02 L11.85,4.02 Z" transform="translate(4)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(198,36)"><path fill="#000000" fill-rule="evenodd" d="M0,0 L14,0 L14,2 L0,2 L0,0 Z M0,4 L6,4 L6,6 L0,6 L0,4 Z M0,8 L2,8 L2,10 L0,10 L0,8 Z M8,4 L14,4 L14,6 L8,6 L8,4 Z M4,8 L6,8 L6,10 L4,10 L4,8 Z M8,8 L10,8 L10,10 L8,10 L8,8 Z M12,8 L14,8 L14,10 L12,10 L12,8 Z" transform="translate(2 4)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(216,36)"><polygon fill="#000000" fill-rule="evenodd" points="-2 2 2 6 6 2" transform="rotate(90 3 10)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(234,36)"><polygon fill="#000000" fill-rule="evenodd" points="7.53 1.53 6.47 .47 4 2.94 1.53 .47 .47 1.53 2.94 4 .47 6.47 1.53 7.53 4 5.06 6.47 7.53 7.53 6.47 5.06 4" transform="translate(5 5)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(0,54)"><polygon fill="#000000" fill-rule="evenodd" points="8.44 .44 5 3.88 1.56 .44 .5 1.5 5 6 9.5 1.5" transform="translate(4 6)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(18,54)"><polygon fill="#000000" fill-rule="evenodd" points="5 0 .5 4.5 1.56 5.56 5 2.12 8.44 5.56 9.5 4.5" transform="translate(4 6)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(36,54)"><polygon fill="#000000" fill-rule="evenodd" points="8.44 .44 5 3.88 1.56 .44 .5 1.5 5 6 9.5 1.5" transform="rotate(90 4 8)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(54,54)"><polygon fill="#000000" fill-rule="evenodd" points="5 0 .5 4.5 1.56 5.56 5 2.12 8.44 5.56 9.5 4.5" transform="rotate(90 4 8)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(72,54)"><polygon fill="#000000" fill-rule="evenodd" points="12 5 3.125 5 7.06 1.06 6 0 0 6 6 12 7.06 10.94 3.125 7 12 7" transform="matrix(-1 0 0 1 15 3)"/>
|
||||
</g>
|
||||
<g xmlns="http://www.w3.org/2000/svg" transform="translate(90,54)"><polygon fill="#000000" fill-rule="evenodd" points="12 5 3.125 5 7.06 1.06 6 0 0 6 6 12 7.06 10.94 3.125 7 12 7" transform="translate(3 3)"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 20 KiB |
507
OrangeFormsOpen-VUE3/src/components/SpreadSheet/canvas/draw.js
Normal file
507
OrangeFormsOpen-VUE3/src/components/SpreadSheet/canvas/draw.js
Normal file
@@ -0,0 +1,507 @@
|
||||
const imageMap = new Map();
|
||||
|
||||
function dpr() {
|
||||
return window.devicePixelRatio || 1;
|
||||
}
|
||||
|
||||
function thinLineWidth() {
|
||||
return dpr() - 0.5;
|
||||
}
|
||||
|
||||
function npx(px) {
|
||||
return parseInt(px * dpr(), 10);
|
||||
}
|
||||
|
||||
function npxLine(px) {
|
||||
const n = npx(px);
|
||||
return n > 0 ? n - 0.5 : 0.5;
|
||||
}
|
||||
|
||||
class DrawBox {
|
||||
constructor(x, y, w, h, padding = 0) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.width = w;
|
||||
this.height = h;
|
||||
this.padding = padding;
|
||||
this.bgcolor = '#ffffff';
|
||||
// border: [width, style, color]
|
||||
this.borderTop = null;
|
||||
this.borderRight = null;
|
||||
this.borderBottom = null;
|
||||
this.borderLeft = null;
|
||||
}
|
||||
|
||||
setBorders({ top, bottom, left, right }) {
|
||||
if (top) this.borderTop = top;
|
||||
if (right) this.borderRight = right;
|
||||
if (bottom) this.borderBottom = bottom;
|
||||
if (left) this.borderLeft = left;
|
||||
}
|
||||
|
||||
innerWidth() {
|
||||
return this.width - this.padding * 2 - 2;
|
||||
}
|
||||
|
||||
innerHeight() {
|
||||
return this.height - this.padding * 2 - 2;
|
||||
}
|
||||
|
||||
textx(align) {
|
||||
const { width, padding } = this;
|
||||
let { x } = this;
|
||||
if (align === 'left') {
|
||||
x += padding;
|
||||
} else if (align === 'center') {
|
||||
x += width / 2;
|
||||
} else if (align === 'right') {
|
||||
x += width - padding;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
texty(align, h) {
|
||||
const { height, padding } = this;
|
||||
let { y } = this;
|
||||
if (align === 'top') {
|
||||
y += padding;
|
||||
} else if (align === 'middle') {
|
||||
y += height / 2 - h / 2;
|
||||
} else if (align === 'bottom') {
|
||||
y += height - padding - h;
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
topxys() {
|
||||
const { x, y, width } = this;
|
||||
return [
|
||||
[x, y],
|
||||
[x + width, y],
|
||||
];
|
||||
}
|
||||
|
||||
rightxys() {
|
||||
const { x, y, width, height } = this;
|
||||
return [
|
||||
[x + width, y],
|
||||
[x + width, y + height],
|
||||
];
|
||||
}
|
||||
|
||||
bottomxys() {
|
||||
const { x, y, width, height } = this;
|
||||
return [
|
||||
[x, y + height],
|
||||
[x + width, y + height],
|
||||
];
|
||||
}
|
||||
|
||||
leftxys() {
|
||||
const { x, y, height } = this;
|
||||
return [
|
||||
[x, y],
|
||||
[x, y + height],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
function drawFontLine(type, tx, ty, align, valign, blheight, blwidth) {
|
||||
const floffset = { x: 0, y: 0 };
|
||||
if (type === 'underline') {
|
||||
if (valign === 'bottom') {
|
||||
floffset.y = 0;
|
||||
} else if (valign === 'top') {
|
||||
floffset.y = -(blheight + 2);
|
||||
} else {
|
||||
floffset.y = -blheight / 2;
|
||||
}
|
||||
} else if (type === 'strike') {
|
||||
if (valign === 'bottom') {
|
||||
floffset.y = blheight / 2;
|
||||
} else if (valign === 'top') {
|
||||
floffset.y = -(blheight / 2 + 2);
|
||||
}
|
||||
}
|
||||
|
||||
if (align === 'center') {
|
||||
floffset.x = blwidth / 2;
|
||||
} else if (align === 'right') {
|
||||
floffset.x = blwidth;
|
||||
}
|
||||
this.line([tx - floffset.x, ty - floffset.y], [tx - floffset.x + blwidth, ty - floffset.y]);
|
||||
}
|
||||
|
||||
class Draw {
|
||||
constructor(el, width, height) {
|
||||
this.el = el;
|
||||
this.ctx = el.getContext('2d');
|
||||
this.resize(width, height);
|
||||
this.ctx.scale(dpr(), dpr());
|
||||
}
|
||||
|
||||
resize(width, height) {
|
||||
// console.log('dpr:', dpr);
|
||||
this.el.style.width = `${width}px`;
|
||||
this.el.style.height = `${height}px`;
|
||||
this.el.width = npx(width);
|
||||
this.el.height = npx(height);
|
||||
}
|
||||
|
||||
clear() {
|
||||
const { width, height } = this.el;
|
||||
this.ctx.clearRect(0, 0, width, height);
|
||||
return this;
|
||||
}
|
||||
|
||||
attr(options) {
|
||||
Object.assign(this.ctx, options);
|
||||
return this;
|
||||
}
|
||||
|
||||
save() {
|
||||
this.ctx.save();
|
||||
this.ctx.beginPath();
|
||||
return this;
|
||||
}
|
||||
|
||||
restore() {
|
||||
this.ctx.restore();
|
||||
return this;
|
||||
}
|
||||
|
||||
beginPath() {
|
||||
this.ctx.beginPath();
|
||||
return this;
|
||||
}
|
||||
|
||||
translate(x, y) {
|
||||
this.ctx.translate(npx(x), npx(y));
|
||||
return this;
|
||||
}
|
||||
|
||||
scale(x, y) {
|
||||
this.ctx.scale(x, y);
|
||||
return this;
|
||||
}
|
||||
|
||||
clearRect(x, y, w, h) {
|
||||
this.ctx.clearRect(x, y, w, h);
|
||||
return this;
|
||||
}
|
||||
|
||||
fillRect(x, y, w, h) {
|
||||
this.ctx.fillRect(npx(x) - 0.5, npx(y) - 0.5, npx(w), npx(h));
|
||||
return this;
|
||||
}
|
||||
|
||||
fillText(text, x, y) {
|
||||
this.ctx.fillText(text, npx(x), npx(y));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <20><>ͼƬ<CDBC><C6AC>
|
||||
* @param {*} box - һ<><D2BB> DrawBox <20><><EFBFBD><EFBFBD>
|
||||
* @param {string} src - ͼƬ<CDBC><C6AC>·<EFBFBD><C2B7>
|
||||
* @param {Object} fixedIndexWidth - <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
* @param {Object} fixedIndexHeight - <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߶<EFBFBD>
|
||||
*/
|
||||
fillImage(box, { value: src }, { fixedIndexWidth, fixedIndexHeight }, scroll, celldata) {
|
||||
if (!celldata.imagewidth) {
|
||||
return;
|
||||
}
|
||||
if (celldata.value == '' || celldata.value == undefined) {
|
||||
imageMap.forEach((value, key) => {
|
||||
if (value[0] === celldata.scaledWidth && value[1] === celldata.scaledHeight) {
|
||||
imageMap.delete(key);
|
||||
}
|
||||
});
|
||||
}
|
||||
const imageTop = celldata.top;
|
||||
const imageLeft = celldata.left;
|
||||
const { x, y, width, height } = box;
|
||||
// if(!((imageTop ==y && imageLeft == x) || (imageTop ==y-scroll.scroll.y && imageLeft == x-scroll.scroll.x)))
|
||||
// {return}
|
||||
const img = new Image();
|
||||
img.src = src;
|
||||
img.onload = () => {
|
||||
this.ctx.save();
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ͻ<EFBFBD>λ<EFBFBD>ã<EFBFBD>Ϊʲôtranslateû<65><C3BB><EFBFBD><EFBFBD>Ч<EFBFBD>أ<EFBFBD><D8A3><EFBFBD>Ϊ<EFBFBD>첽<EFBFBD><ECB2BD><EFBFBD><EFBFBD>
|
||||
let sx = x + fixedIndexWidth;
|
||||
let sy = y + fixedIndexHeight;
|
||||
if (scroll) {
|
||||
sx = sx - scroll.scroll.x + 0;
|
||||
sy = sy - scroll.scroll.y + 0;
|
||||
}
|
||||
//<2F><><EFBFBD>㳤<EFBFBD><E3B3A4><EFBFBD><EFBFBD>
|
||||
const imageWidth = celldata.imagewidth;
|
||||
const imageHeight = celldata.imageheight;
|
||||
const imageH = height - 2; // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʵ<EFBFBD>ʿ<EFBFBD><CABF>߱ȣ<DFB1>ֱ<EFBFBD><D6B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ԫ<EFBFBD><D4AA>
|
||||
const gridCellWidth = width;
|
||||
const gridCellHeight = height;
|
||||
let widthRatio = gridCellWidth / imageWidth;
|
||||
let heightRatio = gridCellHeight / imageHeight;
|
||||
let scaleRatio = Math.min(widthRatio, heightRatio);
|
||||
let scaledWidth = imageWidth * scaleRatio;
|
||||
let scaledHeight = imageHeight * scaleRatio;
|
||||
if (imageMap.has(img.src)) {
|
||||
// scaledWidth = imageMap.get(img.src)[0]
|
||||
// scaledHeight = imageMap.get(img.src)[1]
|
||||
// celldata.scaledWidth = scaledWidth
|
||||
// celldata.scaledHeight = scaledHeight
|
||||
// celldata.scaleRatio = scaleRatio
|
||||
|
||||
//<2F><><EFBFBD><EFBFBD>ͼƬʱ<C6AC>Ѵ<EFBFBD><D1B4><EFBFBD>ƫ<EFBFBD><C6AB>
|
||||
if (imageMap.get(img.src)[2] != 0 || imageMap.get(img.src)[3] != 0) {
|
||||
//<2F><><EFBFBD>¼<EFBFBD><C2BC><EFBFBD><EFBFBD>ʼλ<CABC><CEBB>
|
||||
// if (scroll.scroll.x > imageMap.get(img.src)[2]) {
|
||||
// sx = sx + (scroll.scroll.x - imageMap.get(img.src)[2])
|
||||
// } else {
|
||||
// sx = sx - (imageMap.get(img.src)[2] - scroll.scroll.x)
|
||||
// }
|
||||
// if (scroll.scroll.y > imageMap.get(img.src)[3]) {
|
||||
// sy = sy + (scroll.scroll.y - imageMap.get(img.src)[3])
|
||||
// } else {
|
||||
// sy = sy - (imageMap.get(img.src)[3] - scroll.scroll.y)
|
||||
// }
|
||||
// sx = sx - (scroll.scroll.x - imageMap.get(img.src)[2]) - 5
|
||||
// sy = sy - (scroll.scroll.y - imageMap.get(img.src)[3]) - 5
|
||||
// <20><>ֹ<EFBFBD>ظ<EFBFBD><D8B8><EFBFBD>Ⱦ
|
||||
if (imageTop == y - scroll.scroll.y && imageLeft == x - scroll.scroll.x) {
|
||||
this.ctx.drawImage(img, npx(sx), npx(sy), npx(imageWidth), npx(imageH));
|
||||
this.ctx.restore();
|
||||
}
|
||||
} else {
|
||||
// if (imageTop == y && imageLeft == x) {
|
||||
this.ctx.drawImage(img, npx(sx), npx(sy), npx(imageWidth), npx(imageH));
|
||||
this.ctx.restore();
|
||||
// }
|
||||
}
|
||||
} else {
|
||||
// imageMap.set(img.src, [scaledWidth, scaledHeight, scroll.scroll.x, scroll.scroll.y])
|
||||
// celldata.scaledWidth = scaledWidth
|
||||
// celldata.scaledHeight = scaledHeight
|
||||
// celldata.scaleRatio = scaleRatio
|
||||
// if (imageTop == y - scroll.scroll.y && imageLeft == x - scroll.scroll.x) {
|
||||
this.ctx.drawImage(img, npx(sx), npx(sy), npx(imageWidth), npx(imageH));
|
||||
this.ctx.restore();
|
||||
// }
|
||||
}
|
||||
};
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <20><><EFBFBD><EFBFBD>ͼ<EFBFBD>Ρ<EFBFBD>
|
||||
* <20><><EFBFBD><EFBFBD><EFBFBD>ﱾ<EFBFBD><EFB1BE><EFBFBD><EFBFBD><EFBFBD>ο<EFBFBD>text<78><74><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><DFBC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ԫ<EFBFBD><D4AA>Ϊ radio, checkbox, date ʱ<><CAB1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǰ<EFBFBD><C7B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ӧ<EFBFBD><D3A6>ͼ<EFBFBD>Ρ<EFBFBD>
|
||||
* @param {Object} cell - <20><>Ԫ<EFBFBD><D4AA>
|
||||
* @param {Object} box - DrawBox
|
||||
* @param {Object} fixedIndexWidth - <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
* @param {Object} fixedIndexHeight - <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߶<EFBFBD>
|
||||
* @returns {Draw} CanvasRenderingContext2D ʵ<><CAB5>
|
||||
*/
|
||||
async geometry(cell, box, { fixedIndexWidth, fixedIndexHeight }, style, scroll, celldata) {
|
||||
const { type } = cell;
|
||||
switch (type) {
|
||||
case 'image':
|
||||
await this.fillImage(box, cell, { fixedIndexWidth, fixedIndexHeight }, scroll, celldata);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
txt: render text
|
||||
box: DrawBox
|
||||
attr: {
|
||||
align: left | center | right
|
||||
valign: top | middle | bottom
|
||||
color: '#333333',
|
||||
strike: false,
|
||||
font: {
|
||||
name: 'Arial',
|
||||
size: 14,
|
||||
bold: false,
|
||||
italic: false,
|
||||
}
|
||||
}
|
||||
textWrap: text wrapping
|
||||
*/
|
||||
text(mtxt, box, attr = {}, textWrap = true) {
|
||||
const { ctx } = this;
|
||||
const { align, valign, font, color, strike, underline } = attr;
|
||||
const tx = box.textx(align);
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
this.attr({
|
||||
textAlign: align,
|
||||
textBaseline: valign,
|
||||
font: `${font.italic ? 'italic' : ''} ${font.bold ? 'bold' : ''} ${npx(font.size)}px ${
|
||||
font.name
|
||||
}`,
|
||||
fillStyle: color,
|
||||
strokeStyle: color,
|
||||
});
|
||||
const txts = `${mtxt}`.split('\n');
|
||||
const biw = box.innerWidth();
|
||||
const ntxts = [];
|
||||
txts.forEach(it => {
|
||||
const txtWidth = ctx.measureText(it).width;
|
||||
if (textWrap && txtWidth > npx(biw)) {
|
||||
let textLine = { w: 0, len: 0, start: 0 };
|
||||
for (let i = 0; i < it.length; i += 1) {
|
||||
if (textLine.w >= npx(biw)) {
|
||||
ntxts.push(it.substr(textLine.start, textLine.len));
|
||||
textLine = { w: 0, len: 0, start: i };
|
||||
}
|
||||
textLine.len += 1;
|
||||
textLine.w += ctx.measureText(it[i]).width + 1;
|
||||
}
|
||||
if (textLine.len > 0) {
|
||||
ntxts.push(it.substr(textLine.start, textLine.len));
|
||||
}
|
||||
} else {
|
||||
ntxts.push(it);
|
||||
}
|
||||
});
|
||||
const txtHeight = (ntxts.length - 1) * (font.size + 2);
|
||||
let ty = box.texty(valign, txtHeight);
|
||||
ntxts.forEach(txt => {
|
||||
const txtWidth = ctx.measureText(txt).width;
|
||||
this.fillText(txt, tx, ty);
|
||||
if (strike) {
|
||||
drawFontLine.call(this, 'strike', tx, ty, align, valign, font.size, txtWidth);
|
||||
}
|
||||
if (underline) {
|
||||
drawFontLine.call(this, 'underline', tx, ty, align, valign, font.size, txtWidth);
|
||||
}
|
||||
ty += font.size + 2;
|
||||
});
|
||||
ctx.restore();
|
||||
return this;
|
||||
}
|
||||
|
||||
border(style, color) {
|
||||
const { ctx } = this;
|
||||
ctx.lineWidth = thinLineWidth;
|
||||
ctx.strokeStyle = color;
|
||||
// console.log('style:', style);
|
||||
if (style === 'medium') {
|
||||
ctx.lineWidth = npx(2) - 0.5;
|
||||
} else if (style === 'thick') {
|
||||
ctx.lineWidth = npx(3);
|
||||
} else if (style === 'dashed') {
|
||||
ctx.setLineDash([npx(3), npx(2)]);
|
||||
} else if (style === 'dotted') {
|
||||
ctx.setLineDash([npx(1), npx(1)]);
|
||||
} else if (style === 'double') {
|
||||
ctx.setLineDash([npx(2), 0]);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
line(...xys) {
|
||||
const { ctx } = this;
|
||||
if (xys.length > 1) {
|
||||
ctx.beginPath();
|
||||
const [x, y] = xys[0];
|
||||
ctx.moveTo(npxLine(x), npxLine(y));
|
||||
for (let i = 1; i < xys.length; i += 1) {
|
||||
const [x1, y1] = xys[i];
|
||||
ctx.lineTo(npxLine(x1), npxLine(y1));
|
||||
}
|
||||
ctx.stroke();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
strokeBorders(box) {
|
||||
const { ctx } = this;
|
||||
ctx.save();
|
||||
// border
|
||||
const { borderTop, borderRight, borderBottom, borderLeft } = box;
|
||||
if (borderTop) {
|
||||
this.border(...borderTop);
|
||||
// console.log('box.topxys:', box.topxys());
|
||||
this.line(...box.topxys());
|
||||
}
|
||||
if (borderRight) {
|
||||
this.border(...borderRight);
|
||||
this.line(...box.rightxys());
|
||||
}
|
||||
if (borderBottom) {
|
||||
this.border(...borderBottom);
|
||||
this.line(...box.bottomxys());
|
||||
}
|
||||
if (borderLeft) {
|
||||
this.border(...borderLeft);
|
||||
this.line(...box.leftxys());
|
||||
}
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
dropdown(box) {
|
||||
const { ctx } = this;
|
||||
const { x, y, width, height } = box;
|
||||
const sx = x + width - 15;
|
||||
const sy = y + height - 15;
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(npx(sx), npx(sy));
|
||||
ctx.lineTo(npx(sx + 8), npx(sy));
|
||||
ctx.lineTo(npx(sx + 4), npx(sy + 6));
|
||||
ctx.closePath();
|
||||
ctx.fillStyle = 'rgba(0, 0, 0, .45)';
|
||||
ctx.fill();
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
error(box) {
|
||||
const { ctx } = this;
|
||||
const { x, y, width } = box;
|
||||
const sx = x + width - 1;
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(npx(sx - 8), npx(y - 1));
|
||||
ctx.lineTo(npx(sx), npx(y - 1));
|
||||
ctx.lineTo(npx(sx), npx(y + 8));
|
||||
ctx.closePath();
|
||||
ctx.fillStyle = 'rgba(255, 0, 0, .65)';
|
||||
ctx.fill();
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
frozen(box) {
|
||||
const { ctx } = this;
|
||||
const { x, y, width } = box;
|
||||
const sx = x + width - 1;
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(npx(sx - 8), npx(y - 1));
|
||||
ctx.lineTo(npx(sx), npx(y - 1));
|
||||
ctx.lineTo(npx(sx), npx(y + 8));
|
||||
ctx.closePath();
|
||||
ctx.fillStyle = 'rgba(0, 255, 0, .85)';
|
||||
ctx.fill();
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
rect(box, dtextcb) {
|
||||
const { ctx } = this;
|
||||
const { x, y, width, height, bgcolor } = box;
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.fillStyle = bgcolor || '#fff';
|
||||
ctx.rect(npxLine(x + 1), npxLine(y + 1), npx(width - 2), npx(height - 2));
|
||||
ctx.clip();
|
||||
ctx.fill();
|
||||
dtextcb();
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
|
||||
export default {};
|
||||
export { Draw, DrawBox, thinLineWidth, npx };
|
||||
@@ -0,0 +1,90 @@
|
||||
class Draw {
|
||||
constructor(el) {
|
||||
this.el = el;
|
||||
this.ctx = el.getContext('2d');
|
||||
}
|
||||
|
||||
clear() {
|
||||
const { width, height } = this.el;
|
||||
this.ctx.clearRect(0, 0, width, height);
|
||||
return this;
|
||||
}
|
||||
|
||||
attr(m) {
|
||||
Object.assign(this.ctx, m);
|
||||
return this;
|
||||
}
|
||||
|
||||
save() {
|
||||
this.ctx.save();
|
||||
this.ctx.beginPath();
|
||||
return this;
|
||||
}
|
||||
|
||||
restore() {
|
||||
this.ctx.restore();
|
||||
return this;
|
||||
}
|
||||
|
||||
beginPath() {
|
||||
this.ctx.beginPath();
|
||||
return this;
|
||||
}
|
||||
|
||||
closePath() {
|
||||
this.ctx.closePath();
|
||||
return this;
|
||||
}
|
||||
|
||||
measureText(text) {
|
||||
return this.ctx.measureText(text);
|
||||
}
|
||||
|
||||
rect(x, y, width, height) {
|
||||
this.ctx.rect(x, y, width, height);
|
||||
return this;
|
||||
}
|
||||
|
||||
scale(x, y) {
|
||||
this.ctx.scale(x, y);
|
||||
return this;
|
||||
}
|
||||
|
||||
rotate(angle) {
|
||||
this.ctx.rotate(angle);
|
||||
return this;
|
||||
}
|
||||
|
||||
translate(x, y) {
|
||||
this.ctx.translate(x, y);
|
||||
return this;
|
||||
}
|
||||
|
||||
transform(a, b, c, d, e) {
|
||||
this.ctx.transform(a, b, c, d, e);
|
||||
return this;
|
||||
}
|
||||
|
||||
fillRect(x, y, w, h) {
|
||||
this.ctx.fillRect(x, y, w, h);
|
||||
return this;
|
||||
}
|
||||
|
||||
strokeRect(x, y, w, h) {
|
||||
this.ctx.strokeRect(x, y, w, h);
|
||||
return this;
|
||||
}
|
||||
|
||||
fillText(text, x, y, maxWidth) {
|
||||
this.ctx.fillText(text, x, y, maxWidth);
|
||||
return this;
|
||||
}
|
||||
|
||||
strokeText(text, x, y, maxWidth) {
|
||||
this.ctx.strokeText(text, x, y, maxWidth);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export default {};
|
||||
export { Draw };
|
||||
@@ -0,0 +1,62 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import { h } from './element.js';
|
||||
import Icon from './icon.js';
|
||||
import DropdownColor from './dropdown_color.js';
|
||||
import DropdownLineType from './dropdown_linetype.js';
|
||||
|
||||
function buildTable(...trs) {
|
||||
return h('table', '').child(h('tbody', '').children(...trs));
|
||||
}
|
||||
|
||||
function buildTd(iconName) {
|
||||
return h('td', '').child(
|
||||
h('div', `${cssPrefix}-border-palette-cell`)
|
||||
.child(new Icon(`border-${iconName}`))
|
||||
.on('click', () => {
|
||||
this.mode = iconName;
|
||||
const { mode, style, color } = this;
|
||||
this.change({ mode, style, color });
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
export default class BorderPalette {
|
||||
constructor() {
|
||||
this.color = '#000';
|
||||
this.style = 'thin';
|
||||
this.mode = 'all';
|
||||
this.change = () => {
|
||||
console.log('empty function');
|
||||
};
|
||||
this.ddColor = new DropdownColor('line-color', this.color);
|
||||
this.ddColor.change = color => {
|
||||
this.color = color;
|
||||
};
|
||||
this.ddType = new DropdownLineType(this.style);
|
||||
this.ddType.change = ([s]) => {
|
||||
this.style = s;
|
||||
};
|
||||
this.el = h('div', `${cssPrefix}-border-palette`);
|
||||
const table = buildTable(
|
||||
h('tr', '').children(
|
||||
h('td', `${cssPrefix}-border-palette-left`).child(
|
||||
buildTable(
|
||||
h('tr', '').children(
|
||||
...['all', 'inside', 'horizontal', 'vertical', 'outside'].map(it =>
|
||||
buildTd.call(this, it),
|
||||
),
|
||||
),
|
||||
h('tr', '').children(
|
||||
...['left', 'top', 'right', 'bottom', 'none'].map(it => buildTd.call(this, it)),
|
||||
),
|
||||
),
|
||||
),
|
||||
h('td', `${cssPrefix}-border-palette-right`).children(
|
||||
h('div', `${cssPrefix}-toolbar-btn`).child(this.ddColor.el),
|
||||
h('div', `${cssPrefix}-toolbar-btn`).child(this.ddType.el),
|
||||
),
|
||||
),
|
||||
);
|
||||
this.el.child(table);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import { tf } from '../locale/locale.js';
|
||||
import { h } from './element.js';
|
||||
import { bindClickoutside, unbindClickoutside } from './event.js';
|
||||
import Icon from './icon.js';
|
||||
import FormInput from './form_input.js';
|
||||
import Dropdown from './dropdown.js';
|
||||
// Record: temp not used
|
||||
// import { xtoast } from './message.js';
|
||||
|
||||
class DropdownMore extends Dropdown {
|
||||
constructor(click) {
|
||||
const icon = new Icon('ellipsis');
|
||||
super(icon, 'auto', false, 'top-left');
|
||||
this.contentClick = click;
|
||||
}
|
||||
|
||||
reset(items) {
|
||||
const eles = items.map((it, i) =>
|
||||
h('div', `${cssPrefix}-item`)
|
||||
.css('width', '150px')
|
||||
.css('font-weight', 'normal')
|
||||
.on('click', () => {
|
||||
this.contentClick(i);
|
||||
this.hide();
|
||||
})
|
||||
.child(it),
|
||||
);
|
||||
this.setContentChildren(...eles);
|
||||
}
|
||||
|
||||
setTitle() {
|
||||
console.log('empty function');
|
||||
}
|
||||
}
|
||||
|
||||
const menuItems = [{ key: 'delete', title: tf('contextmenu.deleteSheet') }];
|
||||
|
||||
function buildMenuItem(item) {
|
||||
return h('div', `${cssPrefix}-item`)
|
||||
.child(item.title())
|
||||
.on('click', () => {
|
||||
this.itemClick(item.key);
|
||||
this.hide();
|
||||
});
|
||||
}
|
||||
|
||||
function buildMenu() {
|
||||
return menuItems.map(it => buildMenuItem.call(this, it));
|
||||
}
|
||||
|
||||
class ContextMenu {
|
||||
constructor() {
|
||||
this.el = h('div', `${cssPrefix}-contextmenu`)
|
||||
.css('width', '160px')
|
||||
.children(...buildMenu.call(this))
|
||||
.hide();
|
||||
this.itemClick = () => {
|
||||
console.log('empty function');
|
||||
};
|
||||
}
|
||||
|
||||
hide() {
|
||||
const { el } = this;
|
||||
el.hide();
|
||||
unbindClickoutside(el);
|
||||
}
|
||||
|
||||
setOffset(offset) {
|
||||
const { el } = this;
|
||||
el.offset(offset);
|
||||
el.show();
|
||||
bindClickoutside(el);
|
||||
}
|
||||
}
|
||||
|
||||
export default class Bottombar {
|
||||
constructor(
|
||||
addFunc = () => {
|
||||
console.log('empty function');
|
||||
},
|
||||
swapFunc = () => {
|
||||
console.log('empty function');
|
||||
},
|
||||
deleteFunc = () => {
|
||||
console.log('empty function');
|
||||
},
|
||||
updateFunc = () => {
|
||||
console.log('empty function');
|
||||
},
|
||||
) {
|
||||
this.swapFunc = swapFunc;
|
||||
this.updateFunc = updateFunc;
|
||||
this.dataNames = [];
|
||||
this.activeEl = null;
|
||||
this.deleteEl = null;
|
||||
this.items = [];
|
||||
this.moreEl = new DropdownMore(i => {
|
||||
this.clickSwap2(this.items[i]);
|
||||
});
|
||||
this.contextMenu = new ContextMenu();
|
||||
this.contextMenu.itemClick = deleteFunc;
|
||||
this.el = h('div', `${cssPrefix}-bottombar`).children(
|
||||
this.contextMenu.el,
|
||||
(this.menuEl = h('ul', `${cssPrefix}-menu`).child(
|
||||
h('li', '').children(
|
||||
new Icon('add').on('click', () => {
|
||||
addFunc();
|
||||
}),
|
||||
h('span', '').child(this.moreEl),
|
||||
),
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
addItem(name, active, options) {
|
||||
this.dataNames.push(name);
|
||||
const item = h('li', active ? 'active' : '').child(name);
|
||||
item
|
||||
.on('click', () => {
|
||||
this.clickSwap2(item);
|
||||
})
|
||||
.on('contextmenu', evt => {
|
||||
if (options.mode === 'read') return;
|
||||
const { offsetLeft, offsetHeight } = evt.target;
|
||||
this.contextMenu.setOffset({ left: offsetLeft, bottom: offsetHeight + 1 });
|
||||
this.deleteEl = item;
|
||||
})
|
||||
.on('dblclick', () => {
|
||||
if (options.mode === 'read') return;
|
||||
const v = item.html();
|
||||
const input = new FormInput('auto', '');
|
||||
input.val(v);
|
||||
input.input.on('blur', ({ target }) => {
|
||||
const { value } = target;
|
||||
const nindex = this.dataNames.findIndex(it => it === v);
|
||||
this.renameItem(nindex, value);
|
||||
/*
|
||||
this.dataNames.splice(nindex, 1, value);
|
||||
this.moreEl.reset(this.dataNames);
|
||||
item.html('').child(value);
|
||||
this.updateFunc(nindex, value);
|
||||
*/
|
||||
});
|
||||
item.html('').child(input.el);
|
||||
input.focus();
|
||||
});
|
||||
if (active) {
|
||||
this.clickSwap(item);
|
||||
}
|
||||
this.items.push(item);
|
||||
this.menuEl.child(item);
|
||||
this.moreEl.reset(this.dataNames);
|
||||
}
|
||||
|
||||
renameItem(index, value) {
|
||||
this.dataNames.splice(index, 1, value);
|
||||
this.moreEl.reset(this.dataNames);
|
||||
this.items[index].html('').child(value);
|
||||
this.updateFunc(index, value);
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.items.forEach(it => {
|
||||
this.menuEl.removeChild(it.el);
|
||||
});
|
||||
this.items = [];
|
||||
this.dataNames = [];
|
||||
this.moreEl.reset(this.dataNames);
|
||||
}
|
||||
|
||||
deleteItem() {
|
||||
const { activeEl, deleteEl } = this;
|
||||
if (this.items.length > 1) {
|
||||
const index = this.items.findIndex(it => it === deleteEl);
|
||||
this.items.splice(index, 1);
|
||||
this.dataNames.splice(index, 1);
|
||||
this.menuEl.removeChild(deleteEl.el);
|
||||
this.moreEl.reset(this.dataNames);
|
||||
if (activeEl === deleteEl) {
|
||||
const [f] = this.items;
|
||||
this.activeEl = f;
|
||||
this.activeEl.toggle();
|
||||
return [index, 0];
|
||||
}
|
||||
return [index, -1];
|
||||
}
|
||||
return [-1];
|
||||
}
|
||||
|
||||
clickSwap2(item) {
|
||||
const index = this.items.findIndex(it => it === item);
|
||||
this.clickSwap(item);
|
||||
this.activeEl.toggle();
|
||||
this.swapFunc(index);
|
||||
}
|
||||
|
||||
clickSwap(item) {
|
||||
if (this.activeEl !== null) {
|
||||
this.activeEl.toggle();
|
||||
}
|
||||
this.activeEl = item;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import { t } from '../locale/locale.js';
|
||||
import { Element } from './element.js';
|
||||
|
||||
export default class Button extends Element {
|
||||
// type: primary
|
||||
constructor(title, type = '') {
|
||||
super('div', `${cssPrefix}-button ${type}`);
|
||||
this.child(t(`button.${title}`));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
import { t } from '../locale/locale.js';
|
||||
import { h } from './element.js';
|
||||
import Icon from './icon.js';
|
||||
|
||||
function addMonth(date, step) {
|
||||
date.setMonth(date.getMonth() + step);
|
||||
}
|
||||
|
||||
function weekday(date, index) {
|
||||
const d = new Date(date);
|
||||
d.setDate(index - date.getDay() + 1);
|
||||
return d;
|
||||
}
|
||||
|
||||
function monthDays(year, month, cdate) {
|
||||
// the first day of month
|
||||
const startDate = new Date(year, month, 1, 23, 59, 59);
|
||||
const datess = [[], [], [], [], [], []];
|
||||
for (let i = 0; i < 6; i += 1) {
|
||||
for (let j = 0; j < 7; j += 1) {
|
||||
const index = i * 7 + j;
|
||||
const d = weekday(startDate, index);
|
||||
const disabled = d.getMonth() !== month;
|
||||
// console.log('d:', d, ', cdate:', cdate);
|
||||
const active = d.getMonth() === cdate.getMonth() && d.getDate() === cdate.getDate();
|
||||
datess[i][j] = { d, disabled, active };
|
||||
}
|
||||
}
|
||||
return datess;
|
||||
}
|
||||
|
||||
export default class Calendar {
|
||||
constructor(value) {
|
||||
this.value = value;
|
||||
this.cvalue = new Date(value);
|
||||
|
||||
this.headerLeftEl = h('div', 'calendar-header-left');
|
||||
this.bodyEl = h('tbody', '');
|
||||
this.buildAll();
|
||||
this.el = h('div', 'x-spreadsheet-calendar').children(
|
||||
h('div', 'calendar-header').children(
|
||||
this.headerLeftEl,
|
||||
h('div', 'calendar-header-right').children(
|
||||
h('a', 'calendar-prev')
|
||||
.on('click.stop', () => this.prev())
|
||||
.child(new Icon('chevron-left')),
|
||||
h('a', 'calendar-next')
|
||||
.on('click.stop', () => this.next())
|
||||
.child(new Icon('chevron-right')),
|
||||
),
|
||||
),
|
||||
h('table', 'calendar-body').children(
|
||||
h('thead', '').child(
|
||||
h('tr', '').children(...t('calendar.weeks').map(week => h('th', 'cell').child(week))),
|
||||
),
|
||||
this.bodyEl,
|
||||
),
|
||||
);
|
||||
this.selectChange = () => {
|
||||
console.log('empty function');
|
||||
};
|
||||
}
|
||||
|
||||
setValue(value) {
|
||||
this.value = value;
|
||||
this.cvalue = new Date(value);
|
||||
this.buildAll();
|
||||
}
|
||||
|
||||
prev() {
|
||||
const { value } = this;
|
||||
addMonth(value, -1);
|
||||
this.buildAll();
|
||||
}
|
||||
|
||||
next() {
|
||||
const { value } = this;
|
||||
addMonth(value, 1);
|
||||
this.buildAll();
|
||||
}
|
||||
|
||||
buildAll() {
|
||||
this.buildHeaderLeft();
|
||||
this.buildBody();
|
||||
}
|
||||
|
||||
buildHeaderLeft() {
|
||||
const { value } = this;
|
||||
this.headerLeftEl.html(`${t('calendar.months')[value.getMonth()]} ${value.getFullYear()}`);
|
||||
}
|
||||
|
||||
buildBody() {
|
||||
const { value, cvalue, bodyEl } = this;
|
||||
const mDays = monthDays(value.getFullYear(), value.getMonth(), cvalue);
|
||||
const trs = mDays.map(it => {
|
||||
const tds = it.map(it1 => {
|
||||
let cls = 'cell';
|
||||
if (it1.disabled) cls += ' disabled';
|
||||
if (it1.active) cls += ' active';
|
||||
return h('td', '').child(
|
||||
h('div', cls)
|
||||
.on('click.stop', () => {
|
||||
this.selectChange(it1.d);
|
||||
})
|
||||
.child(it1.d.getDate().toString()),
|
||||
);
|
||||
});
|
||||
return h('tr', '').children(...tds);
|
||||
});
|
||||
bodyEl.html('').children(...trs);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import { h } from './element.js';
|
||||
|
||||
const themeColorPlaceHolders = [
|
||||
'#ffffff',
|
||||
'#000100',
|
||||
'#e7e5e6',
|
||||
'#445569',
|
||||
'#5b9cd6',
|
||||
'#ed7d31',
|
||||
'#a5a5a5',
|
||||
'#ffc001',
|
||||
'#4371c6',
|
||||
'#71ae47',
|
||||
];
|
||||
|
||||
const themeColors = [
|
||||
[
|
||||
'#f2f2f2',
|
||||
'#7f7f7f',
|
||||
'#d0cecf',
|
||||
'#d5dce4',
|
||||
'#deeaf6',
|
||||
'#fce5d5',
|
||||
'#ededed',
|
||||
'#fff2cd',
|
||||
'#d9e2f3',
|
||||
'#e3efd9',
|
||||
],
|
||||
[
|
||||
'#d8d8d8',
|
||||
'#595959',
|
||||
'#afabac',
|
||||
'#adb8ca',
|
||||
'#bdd7ee',
|
||||
'#f7ccac',
|
||||
'#dbdbdb',
|
||||
'#ffe59a',
|
||||
'#b3c6e7',
|
||||
'#c5e0b3',
|
||||
],
|
||||
[
|
||||
'#bfbfbf',
|
||||
'#3f3f3f',
|
||||
'#756f6f',
|
||||
'#8596b0',
|
||||
'#9cc2e6',
|
||||
'#f4b184',
|
||||
'#c9c9c9',
|
||||
'#fed964',
|
||||
'#8eaada',
|
||||
'#a7d08c',
|
||||
],
|
||||
[
|
||||
'#a5a5a5',
|
||||
'#262626',
|
||||
'#3a3839',
|
||||
'#333f4f',
|
||||
'#2e75b5',
|
||||
'#c45a10',
|
||||
'#7b7b7b',
|
||||
'#bf8e01',
|
||||
'#2f5596',
|
||||
'#538136',
|
||||
],
|
||||
[
|
||||
'#7f7f7f',
|
||||
'#0c0c0c',
|
||||
'#171516',
|
||||
'#222a35',
|
||||
'#1f4e7a',
|
||||
'#843c0a',
|
||||
'#525252',
|
||||
'#7e6000',
|
||||
'#203864',
|
||||
'#365624',
|
||||
],
|
||||
];
|
||||
|
||||
const standardColors = [
|
||||
'#c00000',
|
||||
'#fe0000',
|
||||
'#fdc101',
|
||||
'#ffff01',
|
||||
'#93d051',
|
||||
'#00b04e',
|
||||
'#01b0f1',
|
||||
'#0170c1',
|
||||
'#012060',
|
||||
'#7030a0',
|
||||
];
|
||||
|
||||
function buildTd(bgcolor) {
|
||||
return h('td', '').child(
|
||||
h('div', `${cssPrefix}-color-palette-cell`)
|
||||
.on('click.stop', () => this.change(bgcolor))
|
||||
.css('background-color', bgcolor),
|
||||
);
|
||||
}
|
||||
|
||||
export default class ColorPalette {
|
||||
constructor() {
|
||||
this.el = h('div', `${cssPrefix}-color-palette`);
|
||||
this.change = () => {
|
||||
console.log('empty function');
|
||||
};
|
||||
const table = h('table', '').children(
|
||||
h('tbody', '').children(
|
||||
h('tr', `${cssPrefix}-theme-color-placeholders`).children(
|
||||
...themeColorPlaceHolders.map(color => buildTd.call(this, color)),
|
||||
),
|
||||
...themeColors.map(it =>
|
||||
h('tr', `${cssPrefix}-theme-colors`).children(
|
||||
...it.map(color => buildTd.call(this, color)),
|
||||
),
|
||||
),
|
||||
h('tr', `${cssPrefix}-standard-colors`).children(
|
||||
...standardColors.map(color => buildTd.call(this, color)),
|
||||
),
|
||||
),
|
||||
);
|
||||
this.el.child(table);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import { tf } from '../locale/locale.js';
|
||||
import { h } from './element.js';
|
||||
import { bindClickoutside, unbindClickoutside } from './event.js';
|
||||
|
||||
const menuItems = [
|
||||
{ key: 'copy', title: tf('contextmenu.copy'), label: 'Ctrl+C' },
|
||||
{ key: 'cut', title: tf('contextmenu.cut'), label: 'Ctrl+X' },
|
||||
{ key: 'paste', title: tf('contextmenu.paste'), label: 'Ctrl+V' },
|
||||
{ key: 'paste-value', title: tf('contextmenu.pasteValue'), label: 'Ctrl+Shift+V' },
|
||||
{ key: 'paste-format', title: tf('contextmenu.pasteFormat'), label: 'Ctrl+Alt+V' },
|
||||
{ key: 'divider' },
|
||||
{ key: 'insert-row', title: tf('contextmenu.insertRow') },
|
||||
{ key: 'insert-column', title: tf('contextmenu.insertColumn') },
|
||||
{ key: 'divider' },
|
||||
{ key: 'delete-row', title: tf('contextmenu.deleteRow') },
|
||||
{ key: 'delete-column', title: tf('contextmenu.deleteColumn') },
|
||||
{ key: 'delete-cell-text', title: tf('contextmenu.deleteCellText') },
|
||||
{ key: 'hide', title: tf('contextmenu.hide') },
|
||||
{ key: 'divider' },
|
||||
{ key: 'validation', title: tf('contextmenu.validation') },
|
||||
{ key: 'divider' },
|
||||
{ key: 'cell-printable', title: tf('contextmenu.cellprintable') },
|
||||
{ key: 'cell-non-printable', title: tf('contextmenu.cellnonprintable') },
|
||||
{ key: 'divider' },
|
||||
{ key: 'cell-editable', title: tf('contextmenu.celleditable') },
|
||||
{ key: 'cell-non-editable', title: tf('contextmenu.cellnoneditable') },
|
||||
];
|
||||
|
||||
function buildMenuItem(item) {
|
||||
if (item.key === 'divider') {
|
||||
return h('div', `${cssPrefix}-item divider`);
|
||||
}
|
||||
return h('div', `${cssPrefix}-item`)
|
||||
.on('click', () => {
|
||||
this.itemClick(item.key);
|
||||
this.hide();
|
||||
})
|
||||
.children(item.title(), h('div', 'label').child(item.label || ''));
|
||||
}
|
||||
|
||||
function buildMenu() {
|
||||
return menuItems.map(it => buildMenuItem.call(this, it));
|
||||
}
|
||||
|
||||
export default class ContextMenu {
|
||||
constructor(viewFn, isHide = false) {
|
||||
this.menuItems = buildMenu.call(this);
|
||||
this.el = h('div', `${cssPrefix}-contextmenu`)
|
||||
.children(...this.menuItems)
|
||||
.hide();
|
||||
this.viewFn = viewFn;
|
||||
this.itemClick = () => {
|
||||
console.log('empty function');
|
||||
};
|
||||
this.isHide = isHide;
|
||||
this.setMode('range');
|
||||
}
|
||||
|
||||
// row-col: the whole rows or the whole cols
|
||||
// range: select range
|
||||
setMode(mode) {
|
||||
const hideEl = this.menuItems[12];
|
||||
if (mode === 'row-col') {
|
||||
hideEl.show();
|
||||
} else {
|
||||
hideEl.hide();
|
||||
}
|
||||
}
|
||||
|
||||
hide() {
|
||||
const { el } = this;
|
||||
el.hide();
|
||||
unbindClickoutside(el);
|
||||
}
|
||||
|
||||
setPosition(x, y) {
|
||||
if (this.isHide) return;
|
||||
const { el } = this;
|
||||
const { width } = el.show().offset();
|
||||
const view = this.viewFn();
|
||||
const vhf = view.height / 2;
|
||||
let left = x;
|
||||
if (view.width - x <= width) {
|
||||
left -= width;
|
||||
}
|
||||
el.css('left', `${left}px`);
|
||||
if (y > vhf) {
|
||||
el.css('bottom', `${view.height - y}px`)
|
||||
.css('max-height', `${y}px`)
|
||||
.css('top', 'auto');
|
||||
} else {
|
||||
el.css('top', `${y}px`)
|
||||
.css('max-height', `${view.height - y}px`)
|
||||
.css('bottom', 'auto');
|
||||
}
|
||||
bindClickoutside(el);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import Calendar from './calendar.js';
|
||||
import { h } from './element.js';
|
||||
|
||||
export default class Datepicker {
|
||||
constructor() {
|
||||
this.calendar = new Calendar(new Date());
|
||||
this.el = h('div', `${cssPrefix}-datepicker`).child(this.calendar.el).hide();
|
||||
}
|
||||
|
||||
setValue(date) {
|
||||
// console.log(':::::::', date, typeof date, date instanceof string);
|
||||
const { calendar } = this;
|
||||
if (typeof date === 'string') {
|
||||
// console.log(/^\d{4}-\d{1,2}-\d{1,2}$/.test(date));
|
||||
if (/^\d{4}-\d{1,2}-\d{1,2}$/.test(date)) {
|
||||
calendar.setValue(new Date(date.replace(new RegExp('-', 'g'), '/')));
|
||||
}
|
||||
} else if (date instanceof Date) {
|
||||
calendar.setValue(date);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
change(cb) {
|
||||
this.calendar.selectChange = d => {
|
||||
cb(d);
|
||||
this.hide();
|
||||
};
|
||||
}
|
||||
|
||||
show() {
|
||||
this.el.show();
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.el.hide();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import { Element, h } from './element.js';
|
||||
import { bindClickoutside, unbindClickoutside } from './event.js';
|
||||
|
||||
export default class Dropdown extends Element {
|
||||
constructor(title, width, showArrow, placement, ...children) {
|
||||
super('div', `${cssPrefix}-dropdown ${placement}`);
|
||||
this.title = title;
|
||||
this.change = () => {
|
||||
console.log('empty function');
|
||||
};
|
||||
this.headerClick = () => {
|
||||
console.log('empty function');
|
||||
};
|
||||
if (typeof title === 'string') {
|
||||
this.title = h('div', `${cssPrefix}-dropdown-title`).child(title);
|
||||
} else if (showArrow) {
|
||||
this.title.addClass('arrow-left');
|
||||
}
|
||||
this.contentEl = h('div', `${cssPrefix}-dropdown-content`).css('width', width).hide();
|
||||
|
||||
this.setContentChildren(...children);
|
||||
|
||||
this.headerEl = h('div', `${cssPrefix}-dropdown-header`);
|
||||
this.headerEl
|
||||
.on('click', () => {
|
||||
if (this.contentEl.css('display') !== 'block') {
|
||||
this.show();
|
||||
} else {
|
||||
this.hide();
|
||||
}
|
||||
})
|
||||
.children(
|
||||
this.title,
|
||||
showArrow
|
||||
? h('div', `${cssPrefix}-icon arrow-right`).child(
|
||||
h('div', `${cssPrefix}-icon-img arrow-down`),
|
||||
)
|
||||
: '',
|
||||
);
|
||||
this.children(this.headerEl, this.contentEl);
|
||||
}
|
||||
|
||||
setContentChildren(...children) {
|
||||
this.contentEl.html('');
|
||||
if (children.length > 0) {
|
||||
this.contentEl.children(...children);
|
||||
}
|
||||
}
|
||||
|
||||
setTitle(title) {
|
||||
this.title.html(title);
|
||||
this.hide();
|
||||
}
|
||||
|
||||
show() {
|
||||
const { contentEl } = this;
|
||||
contentEl.show();
|
||||
this.parent().active();
|
||||
bindClickoutside(this.parent(), () => {
|
||||
this.hide();
|
||||
});
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.parent().active(false);
|
||||
this.contentEl.hide();
|
||||
unbindClickoutside(this.parent());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import Dropdown from './dropdown.js';
|
||||
import { h } from './element.js';
|
||||
import Icon from './icon.js';
|
||||
|
||||
function buildItemWithIcon(iconName) {
|
||||
return h('div', `${cssPrefix}-item`).child(new Icon(iconName));
|
||||
}
|
||||
|
||||
export default class DropdownAlign extends Dropdown {
|
||||
constructor(aligns, align) {
|
||||
const icon = new Icon(`align-${align}`);
|
||||
const naligns = aligns.map(it =>
|
||||
buildItemWithIcon(`align-${it}`).on('click', () => {
|
||||
this.setTitle(it);
|
||||
this.change(it);
|
||||
}),
|
||||
);
|
||||
super(icon, 'auto', true, 'bottom-left', ...naligns);
|
||||
}
|
||||
|
||||
setTitle(align) {
|
||||
this.title.setName(`align-${align}`);
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import Dropdown from './dropdown.js';
|
||||
import Icon from './icon.js';
|
||||
import BorderPalette from './border_palette.js';
|
||||
|
||||
export default class DropdownBorder extends Dropdown {
|
||||
constructor() {
|
||||
const icon = new Icon('border-all');
|
||||
const borderPalette = new BorderPalette();
|
||||
borderPalette.change = v => {
|
||||
this.change(v);
|
||||
this.hide();
|
||||
};
|
||||
super(icon, 'auto', false, 'bottom-left', borderPalette.el);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import Dropdown from './dropdown.js';
|
||||
import Icon from './icon.js';
|
||||
import ColorPalette from './color_palette.js';
|
||||
|
||||
export default class DropdownColor extends Dropdown {
|
||||
constructor(iconName, color) {
|
||||
const icon = new Icon(iconName)
|
||||
.css('height', '16px')
|
||||
.css('border-bottom', `3px solid ${color}`);
|
||||
const colorPalette = new ColorPalette();
|
||||
colorPalette.change = v => {
|
||||
this.setTitle(v);
|
||||
this.change(v);
|
||||
};
|
||||
super(icon, 'auto', false, 'bottom-left', colorPalette.el);
|
||||
}
|
||||
|
||||
setTitle(color) {
|
||||
this.title.css('border-color', color);
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { baseFonts } from '../core/font.js';
|
||||
import { cssPrefix } from '../config.js';
|
||||
import Dropdown from './dropdown.js';
|
||||
import { h } from './element.js';
|
||||
|
||||
export default class DropdownFont extends Dropdown {
|
||||
constructor() {
|
||||
const nfonts = baseFonts.map(it =>
|
||||
h('div', `${cssPrefix}-item`)
|
||||
.on('click', () => {
|
||||
this.setTitle(it.title);
|
||||
this.change(it);
|
||||
})
|
||||
.child(it.title),
|
||||
);
|
||||
super(baseFonts[0].title, '160px', true, 'bottom-left', ...nfonts);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { fontSizes } from '../core/font.js';
|
||||
import { cssPrefix } from '../config.js';
|
||||
import Dropdown from './dropdown.js';
|
||||
import { h } from './element.js';
|
||||
|
||||
export default class DropdownFontSize extends Dropdown {
|
||||
constructor() {
|
||||
const nfontSizes = fontSizes.map(it =>
|
||||
h('div', `${cssPrefix}-item`)
|
||||
.on('click', () => {
|
||||
this.setTitle(`${it.pt}`);
|
||||
this.change(it);
|
||||
})
|
||||
.child(`${it.pt}`),
|
||||
);
|
||||
super('10', '60px', true, 'bottom-left', ...nfontSizes);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { baseFormats } from '../core/format.js';
|
||||
import { cssPrefix } from '../config.js';
|
||||
import Dropdown from './dropdown.js';
|
||||
import { h } from './element.js';
|
||||
|
||||
export default class DropdownFormat extends Dropdown {
|
||||
constructor() {
|
||||
let nformats = baseFormats.slice(0);
|
||||
nformats.splice(2, 0, { key: 'divider' });
|
||||
nformats.splice(8, 0, { key: 'divider' });
|
||||
nformats = nformats.map(it => {
|
||||
const item = h('div', `${cssPrefix}-item`);
|
||||
if (it.key === 'divider') {
|
||||
item.addClass('divider');
|
||||
} else {
|
||||
item.child(it.title()).on('click', () => {
|
||||
this.setTitle(it.title());
|
||||
this.change(it);
|
||||
});
|
||||
if (it.label) item.child(h('div', 'label').html(it.label));
|
||||
}
|
||||
return item;
|
||||
});
|
||||
super('Normal', '220px', true, 'bottom-left', ...nformats);
|
||||
}
|
||||
|
||||
setTitle(key) {
|
||||
for (let i = 0; i < baseFormats.length; i += 1) {
|
||||
if (baseFormats[i].key === key) {
|
||||
this.title.html(baseFormats[i].title());
|
||||
}
|
||||
}
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { baseFormulas } from '../core/formula.js';
|
||||
import { cssPrefix } from '../config.js';
|
||||
import Dropdown from './dropdown.js';
|
||||
import Icon from './icon.js';
|
||||
import { h } from './element.js';
|
||||
|
||||
export default class DropdownFormula extends Dropdown {
|
||||
constructor() {
|
||||
const nformulas = baseFormulas.map(it =>
|
||||
h('div', `${cssPrefix}-item`)
|
||||
.on('click', () => {
|
||||
this.hide();
|
||||
this.change(it);
|
||||
})
|
||||
.child(it.key),
|
||||
);
|
||||
super(new Icon('formula'), '180px', true, 'bottom-left', ...nformulas);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import Dropdown from './dropdown.js';
|
||||
import { h } from './element.js';
|
||||
import Icon from './icon.js';
|
||||
|
||||
const lineTypes = [
|
||||
[
|
||||
'thin',
|
||||
'<svg xmlns="http://www.w3.org/2000/svg" width="50" height="1" style="user-select: none;"><line x1="0" y1="0.5" x2="50" y2="0.5" stroke-width="1" stroke="black" style="user-select: none;"></line></svg>',
|
||||
],
|
||||
[
|
||||
'medium',
|
||||
'<svg xmlns="http://www.w3.org/2000/svg" width="50" height="2" style="user-select: none;"><line x1="0" y1="1.0" x2="50" y2="1.0" stroke-width="2" stroke="black" style="user-select: none;"></line></svg>',
|
||||
],
|
||||
[
|
||||
'thick',
|
||||
'<svg xmlns="http://www.w3.org/2000/svg" width="50" height="3" style="user-select: none;"><line x1="0" y1="1.5" x2="50" y2="1.5" stroke-width="3" stroke="black" style="user-select: none;"></line></svg>',
|
||||
],
|
||||
[
|
||||
'dashed',
|
||||
'<svg xmlns="http://www.w3.org/2000/svg" width="50" height="1" style="user-select: none;"><line x1="0" y1="0.5" x2="50" y2="0.5" stroke-width="1" stroke="black" stroke-dasharray="2" style="user-select: none;"></line></svg>',
|
||||
],
|
||||
[
|
||||
'dotted',
|
||||
'<svg xmlns="http://www.w3.org/2000/svg" width="50" height="1" style="user-select: none;"><line x1="0" y1="0.5" x2="50" y2="0.5" stroke-width="1" stroke="black" stroke-dasharray="1" style="user-select: none;"></line></svg>',
|
||||
],
|
||||
// ['double', '<svg xmlns="http://www.w3.org/2000/svg" width="50" height="3" style="user-select: none;"><line x1="0" y1="0.5" x2="50" y2="0.5" stroke-width="1" stroke="black" style="user-select: none;"></line><line x1="0" y1="2.5" x2="50" y2="2.5" stroke-width="1" stroke="black" style="user-select: none;"></line></svg>'],
|
||||
];
|
||||
|
||||
export default class DropdownLineType extends Dropdown {
|
||||
constructor(type) {
|
||||
const icon = new Icon('line-type');
|
||||
let beforei = 0;
|
||||
const lineTypeEls = lineTypes.map((it, iti) =>
|
||||
h('div', `${cssPrefix}-item state ${type === it[0] ? 'checked' : ''}`)
|
||||
.on('click', () => {
|
||||
lineTypeEls[beforei].toggle('checked');
|
||||
lineTypeEls[iti].toggle('checked');
|
||||
beforei = iti;
|
||||
this.hide();
|
||||
this.change(it);
|
||||
})
|
||||
.child(h('div', `${cssPrefix}-line-type`).html(it[1])),
|
||||
);
|
||||
|
||||
super(icon, 'auto', false, 'bottom-left', ...lineTypeEls);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import { h } from './element.js';
|
||||
import Suggest from './suggest.js';
|
||||
// TODO datepicker 暂不支持,后续放开datepicker解决报错后使用
|
||||
// import Datepicker from './datepicker';
|
||||
// import { mouseMoveUp } from '../event.js';
|
||||
|
||||
function resetTextareaSize() {
|
||||
const { inputText } = this;
|
||||
if (!/^\s*$/.test(inputText)) {
|
||||
const { textlineEl, textEl, areaOffset } = this;
|
||||
const txts = inputText.split('\n');
|
||||
const maxTxtSize = Math.max(...txts.map(it => it.length));
|
||||
const tlOffset = textlineEl.offset();
|
||||
const fontWidth = tlOffset.width / inputText.length;
|
||||
const tlineWidth = (maxTxtSize + 1) * fontWidth + 5;
|
||||
const maxWidth = this.viewFn().width - areaOffset.left - fontWidth;
|
||||
let h1 = txts.length;
|
||||
if (tlineWidth > areaOffset.width) {
|
||||
let twidth = tlineWidth;
|
||||
if (tlineWidth > maxWidth) {
|
||||
twidth = maxWidth;
|
||||
h1 += parseInt(tlineWidth / maxWidth, 10);
|
||||
h1 += tlineWidth % maxWidth > 0 ? 1 : 0;
|
||||
}
|
||||
textEl.css('width', `${twidth}px`);
|
||||
}
|
||||
h1 *= this.rowHeight;
|
||||
if (h1 > areaOffset.height) {
|
||||
textEl.css('height', `${h1}px`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function insertText({ target }, itxt) {
|
||||
const { value, selectionEnd } = target;
|
||||
const ntxt = `${value.slice(0, selectionEnd)}${itxt}${value.slice(selectionEnd)}`;
|
||||
target.value = ntxt;
|
||||
target.setSelectionRange(selectionEnd + 1, selectionEnd + 1);
|
||||
|
||||
this.inputText = ntxt;
|
||||
this.textlineEl.html(ntxt);
|
||||
resetTextareaSize.call(this);
|
||||
}
|
||||
|
||||
function keydownEventHandler(evt) {
|
||||
const { keyCode, altKey } = evt;
|
||||
if (keyCode !== 13 && keyCode !== 9) evt.stopPropagation();
|
||||
if (keyCode === 13 && altKey) {
|
||||
insertText.call(this, evt, '\n');
|
||||
evt.stopPropagation();
|
||||
}
|
||||
if (keyCode === 13 && !altKey) evt.preventDefault();
|
||||
}
|
||||
|
||||
function inputEventHandler(evt) {
|
||||
const v = evt.target.value;
|
||||
// console.log(evt, 'v:', v);
|
||||
const { suggest, textlineEl, validator } = this;
|
||||
const { cell } = this;
|
||||
if (cell !== null) {
|
||||
if (('editable' in cell && cell.editable === true) || cell.editable === undefined) {
|
||||
this.inputText = v;
|
||||
if (validator) {
|
||||
if (validator.type === 'list') {
|
||||
suggest.search(v);
|
||||
} else {
|
||||
suggest.hide();
|
||||
}
|
||||
} else {
|
||||
const start = v.lastIndexOf('=');
|
||||
if (start !== -1) {
|
||||
suggest.search(v.substring(start + 1));
|
||||
} else {
|
||||
suggest.hide();
|
||||
}
|
||||
}
|
||||
textlineEl.html(v);
|
||||
resetTextareaSize.call(this);
|
||||
this.change('input', v);
|
||||
} else {
|
||||
evt.target.value = cell.text || '';
|
||||
}
|
||||
} else {
|
||||
this.inputText = v;
|
||||
if (validator) {
|
||||
if (validator.type === 'list') {
|
||||
suggest.search(v);
|
||||
} else {
|
||||
suggest.hide();
|
||||
}
|
||||
} else {
|
||||
const start = v.lastIndexOf('=');
|
||||
if (start !== -1) {
|
||||
suggest.search(v.substring(start + 1));
|
||||
} else {
|
||||
suggest.hide();
|
||||
}
|
||||
}
|
||||
textlineEl.html(v);
|
||||
resetTextareaSize.call(this);
|
||||
this.change('input', v);
|
||||
}
|
||||
}
|
||||
|
||||
function setTextareaRange(position) {
|
||||
const { el } = this.textEl;
|
||||
setTimeout(() => {
|
||||
el.focus();
|
||||
el.setSelectionRange(position, position);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
function setText(text, position) {
|
||||
const { textEl, textlineEl } = this;
|
||||
// firefox bug
|
||||
textEl.el.blur();
|
||||
|
||||
textEl.val(text);
|
||||
textlineEl.html(text);
|
||||
setTextareaRange.call(this, position);
|
||||
}
|
||||
|
||||
function suggestItemClick(it) {
|
||||
const { inputText, validator } = this;
|
||||
let position = 0;
|
||||
if (validator && validator.type === 'list') {
|
||||
this.inputText = it;
|
||||
position = this.inputText.length;
|
||||
} else {
|
||||
const start = inputText.lastIndexOf('=');
|
||||
const sit = inputText.substring(0, start + 1);
|
||||
let eit = inputText.substring(start + 1);
|
||||
if (eit.indexOf(')') !== -1) {
|
||||
eit = eit.substring(eit.indexOf(')'));
|
||||
} else {
|
||||
eit = '';
|
||||
}
|
||||
this.inputText = `${sit + it.key}(`;
|
||||
// console.log('inputText:', this.inputText);
|
||||
position = this.inputText.length;
|
||||
this.inputText += `)${eit}`;
|
||||
}
|
||||
setText.call(this, this.inputText, position);
|
||||
}
|
||||
|
||||
function resetSuggestItems() {
|
||||
this.suggest.setItems(this.formulas);
|
||||
}
|
||||
|
||||
function dateFormat(d) {
|
||||
let month = d.getMonth() + 1;
|
||||
let date = d.getDate();
|
||||
if (month < 10) month = `0${month}`;
|
||||
if (date < 10) date = `0${date}`;
|
||||
return `${d.getFullYear()}-${month}-${date}`;
|
||||
}
|
||||
|
||||
export default class Editor {
|
||||
constructor(formulas, viewFn, rowHeight) {
|
||||
this.viewFn = viewFn;
|
||||
this.rowHeight = rowHeight;
|
||||
this.formulas = formulas;
|
||||
this.suggest = new Suggest(formulas, it => {
|
||||
suggestItemClick.call(this, it);
|
||||
});
|
||||
// this.datepicker = new Datepicker();
|
||||
// this.datepicker.change((d) => {
|
||||
// // console.log('d:', d);
|
||||
// this.setText(dateFormat(d));
|
||||
// this.clear();
|
||||
// });
|
||||
this.areaEl = h('div', `${cssPrefix}-editor-area`)
|
||||
.children(
|
||||
(this.textEl = h('textarea', '')
|
||||
.on('input', evt => inputEventHandler.call(this, evt))
|
||||
.on('paste.stop', () => {
|
||||
console.log('empty function');
|
||||
})
|
||||
.on('keydown', evt => keydownEventHandler.call(this, evt))),
|
||||
(this.textlineEl = h('div', 'textline')),
|
||||
this.suggest.el,
|
||||
// this.datepicker.el,
|
||||
)
|
||||
.on('mousemove.stop', () => {
|
||||
console.log('empty function');
|
||||
})
|
||||
.on('mousedown.stop', () => {
|
||||
console.log('empty function');
|
||||
});
|
||||
this.el = h('div', `${cssPrefix}-editor`).child(this.areaEl).hide();
|
||||
this.suggest.bindInputEvents(this.textEl);
|
||||
|
||||
this.areaOffset = null;
|
||||
this.freeze = { w: 0, h: 0 };
|
||||
this.cell = null;
|
||||
this.inputText = '';
|
||||
this.change = () => {
|
||||
console.log('empty function');
|
||||
};
|
||||
}
|
||||
|
||||
setFreezeLengths(width, height) {
|
||||
this.freeze.w = width;
|
||||
this.freeze.h = height;
|
||||
}
|
||||
|
||||
clear() {
|
||||
// const { cell } = this;
|
||||
// const cellText = (cell && cell.text) || '';
|
||||
if (this.inputText !== '') {
|
||||
this.change('finished', this.inputText);
|
||||
}
|
||||
this.cell = null;
|
||||
this.areaOffset = null;
|
||||
this.inputText = '';
|
||||
this.el.hide();
|
||||
this.textEl.val('');
|
||||
this.textlineEl.html('');
|
||||
resetSuggestItems.call(this);
|
||||
// this.datepicker.hide();
|
||||
}
|
||||
|
||||
setOffset(offset, suggestPosition = 'top') {
|
||||
const { textEl, areaEl, suggest, freeze, el } = this;
|
||||
if (offset) {
|
||||
this.areaOffset = offset;
|
||||
const { left, top, width, height, l, t } = offset;
|
||||
// console.log('left:', left, ',top:', top, ', freeze:', freeze);
|
||||
const elOffset = { left: 0, top: 0 };
|
||||
// top left
|
||||
if (freeze.w > l && freeze.h > t) {
|
||||
//
|
||||
} else if (freeze.w < l && freeze.h < t) {
|
||||
elOffset.left = freeze.w;
|
||||
elOffset.top = freeze.h;
|
||||
} else if (freeze.w > l) {
|
||||
elOffset.top = freeze.h;
|
||||
} else if (freeze.h > t) {
|
||||
elOffset.left = freeze.w;
|
||||
}
|
||||
el.offset(elOffset);
|
||||
areaEl.offset({ left: left - elOffset.left - 0.8, top: top - elOffset.top - 0.8 });
|
||||
textEl.offset({ width: width - 9 + 0.8, height: height - 3 + 0.8 });
|
||||
const sOffset = { left: 0 };
|
||||
sOffset[suggestPosition] = height;
|
||||
suggest.setOffset(sOffset);
|
||||
suggest.hide();
|
||||
}
|
||||
}
|
||||
|
||||
setCell(cell, validator) {
|
||||
if (cell && cell.editable === false) return;
|
||||
|
||||
// console.log('::', validator);
|
||||
const { el, datepicker, suggest } = this;
|
||||
el.show();
|
||||
this.cell = cell;
|
||||
const text = (cell && cell.text) || '';
|
||||
this.setText(text);
|
||||
|
||||
this.validator = validator;
|
||||
if (validator) {
|
||||
const { type } = validator;
|
||||
if (type === 'date') {
|
||||
// datepicker.show();
|
||||
if (!/^\s*$/.test(text)) {
|
||||
datepicker.setValue(text);
|
||||
}
|
||||
}
|
||||
if (type === 'list') {
|
||||
suggest.setItems(validator.values());
|
||||
suggest.search('');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setText(text) {
|
||||
this.inputText = text;
|
||||
// console.log('text>>:', text);
|
||||
setText.call(this, text, text.length);
|
||||
resetTextareaSize.call(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
class Element {
|
||||
constructor(tag, className = '') {
|
||||
if (typeof tag === 'string') {
|
||||
this.el = document.createElement(tag);
|
||||
this.el.className = className;
|
||||
} else {
|
||||
this.el = tag;
|
||||
}
|
||||
this.data = {};
|
||||
}
|
||||
|
||||
data(key, value) {
|
||||
if (value !== undefined) {
|
||||
this.data[key] = value;
|
||||
return this;
|
||||
}
|
||||
return this.data[key];
|
||||
}
|
||||
|
||||
on(eventNames, handler) {
|
||||
const [fen, ...oen] = eventNames.split('.');
|
||||
let eventName = fen;
|
||||
if (eventName === 'mousewheel' && /Firefox/i.test(window.navigator.userAgent)) {
|
||||
eventName = 'DOMMouseScroll';
|
||||
}
|
||||
this.el.addEventListener(eventName, evt => {
|
||||
handler(evt);
|
||||
for (let i = 0; i < oen.length; i += 1) {
|
||||
const k = oen[i];
|
||||
if (k === 'left' && evt.button !== 0) {
|
||||
return;
|
||||
}
|
||||
if (k === 'right' && evt.button !== 2) {
|
||||
return;
|
||||
}
|
||||
if (k === 'stop') {
|
||||
evt.stopPropagation();
|
||||
}
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
offset(value) {
|
||||
if (value !== undefined) {
|
||||
Object.keys(value).forEach(k => {
|
||||
this.css(k, `${value[k]}px`);
|
||||
});
|
||||
return this;
|
||||
}
|
||||
const { offsetTop, offsetLeft, offsetHeight, offsetWidth } = this.el;
|
||||
return {
|
||||
top: offsetTop,
|
||||
left: offsetLeft,
|
||||
height: offsetHeight,
|
||||
width: offsetWidth,
|
||||
};
|
||||
}
|
||||
|
||||
scroll(v) {
|
||||
const { el } = this;
|
||||
if (v !== undefined) {
|
||||
if (v.left !== undefined) {
|
||||
el.scrollLeft = v.left;
|
||||
}
|
||||
if (v.top !== undefined) {
|
||||
el.scrollTop = v.top;
|
||||
}
|
||||
}
|
||||
return { left: el.scrollLeft, top: el.scrollTop };
|
||||
}
|
||||
|
||||
box() {
|
||||
return this.el.getBoundingClientRect();
|
||||
}
|
||||
|
||||
parent() {
|
||||
return new Element(this.el.parentNode);
|
||||
}
|
||||
|
||||
children(...eles) {
|
||||
if (arguments.length === 0) {
|
||||
return this.el.childNodes;
|
||||
}
|
||||
eles.forEach(ele => this.child(ele));
|
||||
return this;
|
||||
}
|
||||
|
||||
removeChild(el) {
|
||||
this.el.removeChild(el);
|
||||
}
|
||||
|
||||
/*
|
||||
first() {
|
||||
return this.el.firstChild;
|
||||
}
|
||||
|
||||
last() {
|
||||
return this.el.lastChild;
|
||||
}
|
||||
|
||||
remove(ele) {
|
||||
return this.el.removeChild(ele);
|
||||
}
|
||||
|
||||
prepend(ele) {
|
||||
const { el } = this;
|
||||
if (el.children.length > 0) {
|
||||
el.insertBefore(ele, el.firstChild);
|
||||
} else {
|
||||
el.appendChild(ele);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
prev() {
|
||||
return this.el.previousSibling;
|
||||
}
|
||||
|
||||
next() {
|
||||
return this.el.nextSibling;
|
||||
}
|
||||
*/
|
||||
|
||||
child(arg) {
|
||||
let ele = arg;
|
||||
if (typeof arg === 'string') {
|
||||
ele = document.createTextNode(arg);
|
||||
} else if (arg instanceof Element) {
|
||||
ele = arg.el;
|
||||
}
|
||||
this.el.appendChild(ele);
|
||||
return this;
|
||||
}
|
||||
|
||||
contains(ele) {
|
||||
return this.el.contains(ele);
|
||||
}
|
||||
|
||||
className(v) {
|
||||
if (v !== undefined) {
|
||||
this.el.className = v;
|
||||
return this;
|
||||
}
|
||||
return this.el.className;
|
||||
}
|
||||
|
||||
addClass(name) {
|
||||
this.el.classList.add(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
hasClass(name) {
|
||||
return this.el.classList.contains(name);
|
||||
}
|
||||
|
||||
removeClass(name) {
|
||||
this.el.classList.remove(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
toggle(cls = 'active') {
|
||||
return this.toggleClass(cls);
|
||||
}
|
||||
|
||||
toggleClass(name) {
|
||||
return this.el.classList.toggle(name);
|
||||
}
|
||||
|
||||
active(flag = true, cls = 'active') {
|
||||
if (flag) this.addClass(cls);
|
||||
else this.removeClass(cls);
|
||||
return this;
|
||||
}
|
||||
|
||||
checked(flag = true) {
|
||||
this.active(flag, 'checked');
|
||||
return this;
|
||||
}
|
||||
|
||||
disabled(flag = true) {
|
||||
if (flag) this.addClass('disabled');
|
||||
else this.removeClass('disabled');
|
||||
return this;
|
||||
}
|
||||
|
||||
// key, value
|
||||
// key
|
||||
// {k, v}...
|
||||
attr(key, value) {
|
||||
if (value !== undefined) {
|
||||
this.el.setAttribute(key, value);
|
||||
} else {
|
||||
if (typeof key === 'string') {
|
||||
return this.el.getAttribute(key);
|
||||
}
|
||||
Object.keys(key).forEach(k => {
|
||||
this.el.setAttribute(k, key[k]);
|
||||
});
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
removeAttr(key) {
|
||||
this.el.removeAttribute(key);
|
||||
return this;
|
||||
}
|
||||
|
||||
html(content) {
|
||||
if (content !== undefined) {
|
||||
this.el.innerHTML = content;
|
||||
return this;
|
||||
}
|
||||
return this.el.innerHTML;
|
||||
}
|
||||
|
||||
val(v) {
|
||||
if (v !== undefined) {
|
||||
this.el.value = v;
|
||||
return this;
|
||||
}
|
||||
return this.el.value;
|
||||
}
|
||||
|
||||
focus() {
|
||||
this.el.focus();
|
||||
}
|
||||
|
||||
cssRemoveKeys(...keys) {
|
||||
keys.forEach(k => this.el.style.removeProperty(k));
|
||||
return this;
|
||||
}
|
||||
|
||||
// css( propertyName )
|
||||
// css( propertyName, value )
|
||||
// css( properties )
|
||||
css(name, value) {
|
||||
if (value === undefined && typeof name !== 'string') {
|
||||
Object.keys(name).forEach(k => {
|
||||
this.el.style[k] = name[k];
|
||||
});
|
||||
return this;
|
||||
}
|
||||
if (value !== undefined) {
|
||||
this.el.style[name] = value;
|
||||
return this;
|
||||
}
|
||||
return this.el.style[name];
|
||||
}
|
||||
|
||||
computedStyle() {
|
||||
return window.getComputedStyle(this.el, null);
|
||||
}
|
||||
|
||||
show() {
|
||||
this.css('display', 'block');
|
||||
return this;
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.css('display', 'none');
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
const h = (tag, className = '') => new Element(tag, className);
|
||||
|
||||
export { Element, h };
|
||||
@@ -0,0 +1,148 @@
|
||||
export function bind(target, name, fn) {
|
||||
target.addEventListener(name, fn);
|
||||
}
|
||||
export function unbind(target, name, fn) {
|
||||
target.removeEventListener(name, fn);
|
||||
}
|
||||
export function unbindClickoutside(el) {
|
||||
if (el.xclickoutside) {
|
||||
unbind(window.document.body, 'click', el.xclickoutside);
|
||||
delete el.xclickoutside;
|
||||
}
|
||||
}
|
||||
|
||||
// the left mouse button: mousedown → mouseup → click
|
||||
// the right mouse button: mousedown → contenxtmenu → mouseup
|
||||
// the right mouse button in firefox(>65.0): mousedown → contenxtmenu → mouseup → click on window
|
||||
export function bindClickoutside(el, cb) {
|
||||
el.xclickoutside = evt => {
|
||||
// ignore double click
|
||||
// console.log('evt:', evt);
|
||||
if (evt.detail === 2 || el.contains(evt.target)) return;
|
||||
if (cb) cb(el);
|
||||
else {
|
||||
el.hide();
|
||||
unbindClickoutside(el);
|
||||
}
|
||||
};
|
||||
bind(window.document.body, 'click', el.xclickoutside);
|
||||
}
|
||||
export function mouseMoveUp(target, movefunc, upfunc) {
|
||||
bind(target, 'mousemove', movefunc);
|
||||
const t = target;
|
||||
t.xEvtUp = evt => {
|
||||
// console.log('mouseup>>>');
|
||||
unbind(target, 'mousemove', movefunc);
|
||||
unbind(target, 'mouseup', target.xEvtUp);
|
||||
upfunc(evt);
|
||||
};
|
||||
bind(target, 'mouseup', target.xEvtUp);
|
||||
}
|
||||
|
||||
function calTouchDirection(spanx, spany, evt, cb) {
|
||||
let direction = '';
|
||||
// console.log('spanx:', spanx, ', spany:', spany);
|
||||
if (Math.abs(spanx) > Math.abs(spany)) {
|
||||
// horizontal
|
||||
direction = spanx > 0 ? 'right' : 'left';
|
||||
cb(direction, spanx, evt);
|
||||
} else {
|
||||
// vertical
|
||||
direction = spany > 0 ? 'down' : 'up';
|
||||
cb(direction, spany, evt);
|
||||
}
|
||||
}
|
||||
// cb = (direction, distance) => {}
|
||||
export function bindTouch(target, { move, end }) {
|
||||
let startx = 0;
|
||||
let starty = 0;
|
||||
bind(target, 'touchstart', evt => {
|
||||
const { pageX, pageY } = evt.touches[0];
|
||||
startx = pageX;
|
||||
starty = pageY;
|
||||
});
|
||||
bind(target, 'touchmove', evt => {
|
||||
if (!move) return;
|
||||
const { pageX, pageY } = evt.changedTouches[0];
|
||||
const spanx = pageX - startx;
|
||||
const spany = pageY - starty;
|
||||
if (Math.abs(spanx) > 10 || Math.abs(spany) > 10) {
|
||||
// console.log('spanx:', spanx, ', spany:', spany);
|
||||
calTouchDirection(spanx, spany, evt, move);
|
||||
startx = pageX;
|
||||
starty = pageY;
|
||||
}
|
||||
evt.preventDefault();
|
||||
});
|
||||
bind(target, 'touchend', evt => {
|
||||
if (!end) return;
|
||||
const { pageX, pageY } = evt.changedTouches[0];
|
||||
const spanx = pageX - startx;
|
||||
const spany = pageY - starty;
|
||||
calTouchDirection(spanx, spany, evt, end);
|
||||
});
|
||||
}
|
||||
|
||||
// eventemiter
|
||||
export function createEventEmitter() {
|
||||
const listeners = new Map();
|
||||
|
||||
function on(eventName, callback) {
|
||||
const push = () => {
|
||||
const currentListener = listeners.get(eventName);
|
||||
return (Array.isArray(currentListener) && currentListener.push(callback)) || false;
|
||||
};
|
||||
|
||||
const create = () => listeners.set(eventName, [].concat(callback));
|
||||
|
||||
return (listeners.has(eventName) && push()) || create();
|
||||
}
|
||||
|
||||
function fire(eventName, args) {
|
||||
const exec = () => {
|
||||
const currentListener = listeners.get(eventName);
|
||||
for (const callback of currentListener) callback.call(null, ...args);
|
||||
};
|
||||
|
||||
return listeners.has(eventName) && exec();
|
||||
}
|
||||
|
||||
function removeListener(eventName, callback) {
|
||||
const remove = () => {
|
||||
const currentListener = listeners.get(eventName);
|
||||
const idx = currentListener.indexOf(callback);
|
||||
return (
|
||||
idx >= 0 &&
|
||||
currentListener.splice(idx, 1) &&
|
||||
listeners.get(eventName).length === 0 &&
|
||||
listeners.delete(eventName)
|
||||
);
|
||||
};
|
||||
|
||||
return listeners.has(eventName) && remove();
|
||||
}
|
||||
|
||||
function once(eventName, callback) {
|
||||
const execCalllback = (...args) => {
|
||||
callback.call(null, ...args);
|
||||
removeListener(eventName, execCalllback);
|
||||
};
|
||||
|
||||
return on(eventName, execCalllback);
|
||||
}
|
||||
|
||||
function removeAllListeners() {
|
||||
listeners.clear();
|
||||
}
|
||||
|
||||
return {
|
||||
get current() {
|
||||
return listeners;
|
||||
},
|
||||
on,
|
||||
once,
|
||||
fire,
|
||||
removeListener,
|
||||
removeAllListeners,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import { t } from '../locale/locale.js';
|
||||
import { h } from './element.js';
|
||||
|
||||
const patterns = {
|
||||
number: /(^\d+$)|(^\d+(\.\d{0,4})?$)/,
|
||||
date: /^\d{4}-\d{1,2}-\d{1,2}$/,
|
||||
};
|
||||
|
||||
// rule: { required: false, type, pattern: // }
|
||||
export default class FormField {
|
||||
constructor(input, rule, label, labelWidth) {
|
||||
this.label = '';
|
||||
this.rule = rule;
|
||||
if (label) {
|
||||
this.label = h('label', 'label').css('width', `${labelWidth}px`).html(label);
|
||||
}
|
||||
this.tip = h('div', 'tip').child('tip').hide();
|
||||
this.input = input;
|
||||
this.input.vchange = () => this.validate();
|
||||
this.el = h('div', `${cssPrefix}-form-field`).children(this.label, input.el, this.tip);
|
||||
}
|
||||
|
||||
isShow() {
|
||||
return this.el.css('display') !== 'none';
|
||||
}
|
||||
|
||||
show() {
|
||||
this.el.show();
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.el.hide();
|
||||
return this;
|
||||
}
|
||||
|
||||
val(v) {
|
||||
return this.input.val(v);
|
||||
}
|
||||
|
||||
hint(hint) {
|
||||
this.input.hint(hint);
|
||||
}
|
||||
|
||||
validate() {
|
||||
const { input, rule, tip, el } = this;
|
||||
const v = input.val();
|
||||
if (rule.required) {
|
||||
if (/^\s*$/.test(v)) {
|
||||
tip.html(t('validation.required'));
|
||||
el.addClass('error');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (rule.type || rule.pattern) {
|
||||
const pattern = rule.pattern || patterns[rule.type];
|
||||
if (!pattern.test(v)) {
|
||||
tip.html(t('validation.notMatch'));
|
||||
el.addClass('error');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
el.removeClass('error');
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import { h } from './element.js';
|
||||
|
||||
export default class FormInput {
|
||||
constructor(width, hint) {
|
||||
this.vchange = () => {
|
||||
console.log('empty function');
|
||||
};
|
||||
this.el = h('div', `${cssPrefix}-form-input`);
|
||||
this.input = h('input', '')
|
||||
.css('width', width)
|
||||
.on('input', evt => this.vchange(evt))
|
||||
.attr('placeholder', hint);
|
||||
this.el.child(this.input);
|
||||
}
|
||||
|
||||
focus() {
|
||||
setTimeout(() => {
|
||||
this.input.el.focus();
|
||||
}, 10);
|
||||
}
|
||||
|
||||
hint(v) {
|
||||
this.input.attr('placeholder', v);
|
||||
}
|
||||
|
||||
val(v) {
|
||||
return this.input.val(v);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import { h } from './element.js';
|
||||
import Suggest from './suggest.js';
|
||||
|
||||
export default class FormSelect {
|
||||
constructor(
|
||||
key,
|
||||
items,
|
||||
width,
|
||||
getTitle = it => it,
|
||||
change = () => {
|
||||
console.log('empty function');
|
||||
},
|
||||
) {
|
||||
this.key = key;
|
||||
this.getTitle = getTitle;
|
||||
this.vchange = () => {
|
||||
console.log('empty function');
|
||||
};
|
||||
this.el = h('div', `${cssPrefix}-form-select`);
|
||||
this.suggest = new Suggest(
|
||||
items.map(it => ({ key: it, title: this.getTitle(it) })),
|
||||
it => {
|
||||
this.itemClick(it.key);
|
||||
change(it.key);
|
||||
this.vchange(it.key);
|
||||
},
|
||||
width,
|
||||
this.el,
|
||||
);
|
||||
this.el
|
||||
.children((this.itemEl = h('div', 'input-text').html(this.getTitle(key))), this.suggest.el)
|
||||
.on('click', () => this.show());
|
||||
}
|
||||
|
||||
show() {
|
||||
this.suggest.search('');
|
||||
}
|
||||
|
||||
itemClick(it) {
|
||||
this.key = it;
|
||||
this.itemEl.html(this.getTitle(it));
|
||||
}
|
||||
|
||||
val(v) {
|
||||
if (v !== undefined) {
|
||||
this.key = v;
|
||||
this.itemEl.html(this.getTitle(v));
|
||||
return this;
|
||||
}
|
||||
return this.key;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import { Element, h } from './element.js';
|
||||
|
||||
export default class Icon extends Element {
|
||||
constructor(name) {
|
||||
super('div', `${cssPrefix}-icon`);
|
||||
this.iconNameEl = h('div', `${cssPrefix}-icon-img ${name}`);
|
||||
this.child(this.iconNameEl);
|
||||
}
|
||||
|
||||
setName(name) {
|
||||
this.iconNameEl.className(`${cssPrefix}-icon-img ${name}`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import { h } from './element.js';
|
||||
import Icon from './icon.js';
|
||||
|
||||
export function xtoast(title, content) {
|
||||
const el = h('div', `${cssPrefix}-toast`);
|
||||
const dimmer = h('div', `${cssPrefix}-dimmer active`);
|
||||
const remove = () => {
|
||||
document.body.removeChild(el.el);
|
||||
document.body.removeChild(dimmer.el);
|
||||
};
|
||||
|
||||
el.children(
|
||||
h('div', `${cssPrefix}-toast-header`).children(
|
||||
new Icon('close').on('click.stop', () => remove()),
|
||||
title,
|
||||
),
|
||||
h('div', `${cssPrefix}-toast-content`).html(content),
|
||||
);
|
||||
document.body.appendChild(el.el);
|
||||
document.body.appendChild(dimmer.el);
|
||||
// set offset
|
||||
const { width, height } = el.box();
|
||||
const { clientHeight, clientWidth } = document.documentElement;
|
||||
el.offset({
|
||||
left: (clientWidth - width) / 2,
|
||||
top: (clientHeight - height) / 3,
|
||||
});
|
||||
}
|
||||
|
||||
export default {};
|
||||
@@ -0,0 +1,45 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import { h } from './element.js';
|
||||
import Icon from './icon.js';
|
||||
import { bind, unbind } from './event.js';
|
||||
|
||||
export default class Modal {
|
||||
constructor(title, content, width = '600px') {
|
||||
this.title = title;
|
||||
this.el = h('div', `${cssPrefix}-modal`)
|
||||
.css('width', width)
|
||||
.children(
|
||||
h('div', `${cssPrefix}-modal-header`).children(
|
||||
new Icon('close').on('click.stop', () => this.hide()),
|
||||
this.title,
|
||||
),
|
||||
h('div', `${cssPrefix}-modal-content`).children(...content),
|
||||
)
|
||||
.hide();
|
||||
}
|
||||
|
||||
show() {
|
||||
// dimmer
|
||||
this.dimmer = h('div', `${cssPrefix}-dimmer active`);
|
||||
document.body.appendChild(this.dimmer.el);
|
||||
const { width, height } = this.el.show().box();
|
||||
const { clientHeight, clientWidth } = document.documentElement;
|
||||
this.el.offset({
|
||||
left: (clientWidth - width) / 2,
|
||||
top: (clientHeight - height) / 3,
|
||||
});
|
||||
window.xkeydownEsc = evt => {
|
||||
if (evt.keyCode === 27) {
|
||||
this.hide();
|
||||
}
|
||||
};
|
||||
bind(window, 'keydown', window.xkeydownEsc);
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.el.hide();
|
||||
document.body.removeChild(this.dimmer.el);
|
||||
unbind(window, 'keydown', window.xkeydownEsc);
|
||||
delete window.xkeydownEsc;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
import { t } from '../locale/locale.js';
|
||||
import { cssPrefix } from '../config.js';
|
||||
import Modal from './modal.js';
|
||||
import FormInput from './form_input.js';
|
||||
import FormSelect from './form_select.js';
|
||||
import FormField from './form_field.js';
|
||||
import Button from './button.js';
|
||||
import { h } from './element.js';
|
||||
|
||||
const fieldLabelWidth = 100;
|
||||
|
||||
export default class ModalValidation extends Modal {
|
||||
constructor() {
|
||||
const mf = new FormField(
|
||||
new FormSelect(
|
||||
'cell',
|
||||
['cell'], // cell|row|column
|
||||
'100%',
|
||||
it => t(`dataValidation.modeType.${it}`),
|
||||
),
|
||||
{ required: true },
|
||||
`${t('dataValidation.range')}:`,
|
||||
fieldLabelWidth,
|
||||
);
|
||||
const rf = new FormField(new FormInput('120px', 'E3 or E3:F12'), {
|
||||
required: true,
|
||||
pattern: /^([A-Z]{1,2}[1-9]\d*)(:[A-Z]{1,2}[1-9]\d*)?$/,
|
||||
});
|
||||
const cf = new FormField(
|
||||
new FormSelect(
|
||||
'list',
|
||||
['list', 'number', 'date', 'phone', 'email'],
|
||||
'100%',
|
||||
it => t(`dataValidation.type.${it}`),
|
||||
it => this.criteriaSelected(it),
|
||||
),
|
||||
{ required: true },
|
||||
`${t('dataValidation.criteria')}:`,
|
||||
fieldLabelWidth,
|
||||
);
|
||||
|
||||
// operator
|
||||
const of = new FormField(
|
||||
new FormSelect(
|
||||
'be',
|
||||
['be', 'nbe', 'eq', 'neq', 'lt', 'lte', 'gt', 'gte'],
|
||||
'160px',
|
||||
it => t(`dataValidation.operator.${it}`),
|
||||
it => this.criteriaOperatorSelected(it),
|
||||
),
|
||||
{ required: true },
|
||||
).hide();
|
||||
// min, max
|
||||
const minvf = new FormField(new FormInput('70px', '10'), { required: true }).hide();
|
||||
const maxvf = new FormField(new FormInput('70px', '100'), {
|
||||
required: true,
|
||||
type: 'number',
|
||||
}).hide();
|
||||
// value
|
||||
const svf = new FormField(new FormInput('120px', 'a,b,c'), { required: true });
|
||||
const vf = new FormField(new FormInput('70px', '10'), {
|
||||
required: true,
|
||||
type: 'number',
|
||||
}).hide();
|
||||
|
||||
super(t('contextmenu.validation'), [
|
||||
h('div', `${cssPrefix}-form-fields`).children(mf.el, rf.el),
|
||||
h('div', `${cssPrefix}-form-fields`).children(
|
||||
cf.el,
|
||||
of.el,
|
||||
minvf.el,
|
||||
maxvf.el,
|
||||
vf.el,
|
||||
svf.el,
|
||||
),
|
||||
h('div', `${cssPrefix}-buttons`).children(
|
||||
new Button('cancel').on('click', () => this.btnClick('cancel')),
|
||||
new Button('remove').on('click', () => this.btnClick('remove')),
|
||||
new Button('save', 'primary').on('click', () => this.btnClick('save')),
|
||||
),
|
||||
]);
|
||||
this.mf = mf;
|
||||
this.rf = rf;
|
||||
this.cf = cf;
|
||||
this.of = of;
|
||||
this.minvf = minvf;
|
||||
this.maxvf = maxvf;
|
||||
this.vf = vf;
|
||||
this.svf = svf;
|
||||
this.change = () => {
|
||||
console.log('empty function');
|
||||
};
|
||||
}
|
||||
|
||||
showVf(it) {
|
||||
const hint = it === 'date' ? '2018-11-12' : '10';
|
||||
const { vf } = this;
|
||||
vf.input.hint(hint);
|
||||
vf.show();
|
||||
}
|
||||
|
||||
criteriaSelected(it) {
|
||||
const { of, minvf, maxvf, vf, svf } = this;
|
||||
if (it === 'date' || it === 'number') {
|
||||
of.show();
|
||||
minvf.rule.type = it;
|
||||
maxvf.rule.type = it;
|
||||
if (it === 'date') {
|
||||
minvf.hint('2018-11-12');
|
||||
maxvf.hint('2019-11-12');
|
||||
} else {
|
||||
minvf.hint('10');
|
||||
maxvf.hint('100');
|
||||
}
|
||||
minvf.show();
|
||||
maxvf.show();
|
||||
vf.hide();
|
||||
svf.hide();
|
||||
} else {
|
||||
if (it === 'list') {
|
||||
svf.show();
|
||||
} else {
|
||||
svf.hide();
|
||||
}
|
||||
vf.hide();
|
||||
of.hide();
|
||||
minvf.hide();
|
||||
maxvf.hide();
|
||||
}
|
||||
}
|
||||
|
||||
criteriaOperatorSelected(it) {
|
||||
if (!it) return;
|
||||
const { minvf, maxvf, vf } = this;
|
||||
if (it === 'be' || it === 'nbe') {
|
||||
minvf.show();
|
||||
maxvf.show();
|
||||
vf.hide();
|
||||
} else {
|
||||
const type = this.cf.val();
|
||||
vf.rule.type = type;
|
||||
if (type === 'date') {
|
||||
vf.hint('2018-11-12');
|
||||
} else {
|
||||
vf.hint('10');
|
||||
}
|
||||
vf.show();
|
||||
minvf.hide();
|
||||
maxvf.hide();
|
||||
}
|
||||
}
|
||||
|
||||
btnClick(action) {
|
||||
if (action === 'cancel') {
|
||||
this.hide();
|
||||
} else if (action === 'remove') {
|
||||
this.change('remove');
|
||||
this.hide();
|
||||
} else if (action === 'save') {
|
||||
// validate
|
||||
const attrs = ['mf', 'rf', 'cf', 'of', 'svf', 'vf', 'minvf', 'maxvf'];
|
||||
for (let i = 0; i < attrs.length; i += 1) {
|
||||
const field = this[attrs[i]];
|
||||
// console.log('field:', field);
|
||||
if (field.isShow()) {
|
||||
// console.log('it:', it);
|
||||
if (!field.validate()) return;
|
||||
}
|
||||
}
|
||||
|
||||
const mode = this.mf.val();
|
||||
const ref = this.rf.val();
|
||||
const type = this.cf.val();
|
||||
const operator = this.of.val();
|
||||
let value = this.svf.val();
|
||||
if (type === 'number' || type === 'date') {
|
||||
if (operator === 'be' || operator === 'nbe') {
|
||||
value = [this.minvf.val(), this.maxvf.val()];
|
||||
} else {
|
||||
value = this.vf.val();
|
||||
}
|
||||
}
|
||||
// console.log(mode, ref, type, operator, value);
|
||||
this.change('save', mode, ref, type, operator, {
|
||||
required: false,
|
||||
value,
|
||||
});
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
|
||||
// validation: { mode, ref, validator }
|
||||
setValue(v) {
|
||||
if (v) {
|
||||
const { mf, rf, cf, of, svf, vf, minvf, maxvf } = this;
|
||||
const { mode, ref, validator } = v;
|
||||
const { type, operator, value } = validator || { type: 'list' };
|
||||
mf.val(mode || 'cell');
|
||||
rf.val(ref);
|
||||
cf.val(type);
|
||||
of.val(operator);
|
||||
if (Array.isArray(value)) {
|
||||
minvf.val(value[0]);
|
||||
maxvf.val(value[1]);
|
||||
} else {
|
||||
svf.val(value || '');
|
||||
vf.val(value || '');
|
||||
}
|
||||
this.criteriaSelected(type);
|
||||
this.criteriaOperatorSelected(operator);
|
||||
}
|
||||
this.show();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import { Draw } from '../canvas/draw.js';
|
||||
import { t } from '../locale/locale.js';
|
||||
import { h } from './element.js';
|
||||
import Button from './button.js';
|
||||
import { renderCell } from './table.js';
|
||||
|
||||
// resolution: 72 => 595 x 842
|
||||
// 150 => 1240 x 1754
|
||||
// 200 => 1654 x 2339
|
||||
// 300 => 2479 x 3508
|
||||
// 96 * cm / 2.54 , 96 * cm / 2.54
|
||||
|
||||
const PAGER_SIZES = [
|
||||
['A3', 11.69, 16.54],
|
||||
['A4', 8.27, 11.69],
|
||||
['A5', 5.83, 8.27],
|
||||
['B4', 9.84, 13.9],
|
||||
['B5', 6.93, 9.84],
|
||||
];
|
||||
|
||||
const PAGER_ORIENTATIONS = ['landscape', 'portrait'];
|
||||
|
||||
function inches2px(inc) {
|
||||
return parseInt(96 * inc, 10);
|
||||
}
|
||||
|
||||
function btnClick(type) {
|
||||
if (type === 'cancel') {
|
||||
this.el.hide();
|
||||
} else {
|
||||
this.toPrint();
|
||||
}
|
||||
}
|
||||
|
||||
function pagerSizeChange(evt) {
|
||||
const { paper } = this;
|
||||
const { value } = evt.target;
|
||||
const ps = PAGER_SIZES[value];
|
||||
paper.w = inches2px(ps[1]);
|
||||
paper.h = inches2px(ps[2]);
|
||||
// console.log('paper:', ps, paper);
|
||||
this.preview();
|
||||
}
|
||||
function pagerOrientationChange(evt) {
|
||||
const { paper } = this;
|
||||
const { value } = evt.target;
|
||||
const v = PAGER_ORIENTATIONS[value];
|
||||
paper.orientation = v;
|
||||
this.preview();
|
||||
}
|
||||
|
||||
export default class Print {
|
||||
constructor(data) {
|
||||
this.paper = {
|
||||
w: inches2px(PAGER_SIZES[0][1]),
|
||||
h: inches2px(PAGER_SIZES[0][2]),
|
||||
padding: 50,
|
||||
orientation: PAGER_ORIENTATIONS[0],
|
||||
get width() {
|
||||
return this.orientation === 'landscape' ? this.h : this.w;
|
||||
},
|
||||
get height() {
|
||||
return this.orientation === 'landscape' ? this.w : this.h;
|
||||
},
|
||||
};
|
||||
this.data = data;
|
||||
this.el = h('div', `${cssPrefix}-print`)
|
||||
.children(
|
||||
h('div', `${cssPrefix}-print-bar`).children(
|
||||
h('div', '-title').child('Print settings'),
|
||||
h('div', '-right').children(
|
||||
h('div', `${cssPrefix}-buttons`).children(
|
||||
new Button('cancel').on('click', btnClick.bind(this, 'cancel')),
|
||||
new Button('next', 'primary').on('click', btnClick.bind(this, 'next')),
|
||||
),
|
||||
),
|
||||
),
|
||||
h('div', `${cssPrefix}-print-content`).children(
|
||||
(this.contentEl = h('div', '-content')),
|
||||
h('div', '-sider').child(
|
||||
h('form', '').children(
|
||||
h('fieldset', '').children(
|
||||
h('label', '').child(`${t('print.size')}`),
|
||||
h('select', '')
|
||||
.children(
|
||||
...PAGER_SIZES.map((it, index) =>
|
||||
h('option', '')
|
||||
.attr('value', index)
|
||||
.child(`${it[0]} ( ${it[1]}''x${it[2]}'' )`),
|
||||
),
|
||||
)
|
||||
.on('change', pagerSizeChange.bind(this)),
|
||||
),
|
||||
h('fieldset', '').children(
|
||||
h('label', '').child(`${t('print.orientation')}`),
|
||||
h('select', '')
|
||||
.children(
|
||||
...PAGER_ORIENTATIONS.map((it, index) =>
|
||||
h('option', '')
|
||||
.attr('value', index)
|
||||
.child(`${t('print.orientations')[index]}`),
|
||||
),
|
||||
)
|
||||
.on('change', pagerOrientationChange.bind(this)),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
.hide();
|
||||
}
|
||||
|
||||
resetData(data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
preview() {
|
||||
const { data, paper } = this;
|
||||
const { width, height, padding } = paper;
|
||||
const iwidth = width - padding * 2;
|
||||
const iheight = height - padding * 2;
|
||||
const cr = data.contentRange();
|
||||
const pages = parseInt(cr.h / iheight, 10) + 1;
|
||||
const scale = iwidth / cr.w;
|
||||
let left = padding;
|
||||
const top = padding;
|
||||
if (scale > 1) {
|
||||
left += (iwidth - cr.w) / 2;
|
||||
}
|
||||
let ri = 0;
|
||||
let yoffset = 0;
|
||||
this.contentEl.html('');
|
||||
this.canvases = [];
|
||||
const mViewRange = {
|
||||
sri: 0,
|
||||
sci: 0,
|
||||
eri: 0,
|
||||
eci: 0,
|
||||
};
|
||||
for (let i = 0; i < pages; i += 1) {
|
||||
let th = 0;
|
||||
let yo = 0;
|
||||
const wrap = h('div', `${cssPrefix}-canvas-card`);
|
||||
const canvas = h('canvas', `${cssPrefix}-canvas`);
|
||||
this.canvases.push(canvas.el);
|
||||
const draw = new Draw(canvas.el, width, height);
|
||||
// cell-content
|
||||
draw.save();
|
||||
draw.translate(left, top);
|
||||
if (scale < 1) draw.scale(scale, scale);
|
||||
// console.log('ri:', ri, cr.eri, yoffset);
|
||||
for (; ri <= cr.eri; ri += 1) {
|
||||
const rh = data.rows.getHeight(ri);
|
||||
th += rh;
|
||||
if (th < iheight) {
|
||||
for (let ci = 0; ci <= cr.eci; ci += 1) {
|
||||
renderCell(draw, data, ri, ci, yoffset);
|
||||
mViewRange.eci = ci;
|
||||
}
|
||||
} else {
|
||||
yo = -(th - rh);
|
||||
break;
|
||||
}
|
||||
}
|
||||
mViewRange.eri = ri;
|
||||
draw.restore();
|
||||
// merge-cell
|
||||
draw.save();
|
||||
draw.translate(left, top);
|
||||
if (scale < 1) draw.scale(scale, scale);
|
||||
const yof = yoffset;
|
||||
data.eachMergesInView(mViewRange, ({ sri, sci }) => {
|
||||
renderCell(draw, data, sri, sci, yof);
|
||||
});
|
||||
draw.restore();
|
||||
|
||||
mViewRange.sri = mViewRange.eri;
|
||||
mViewRange.sci = mViewRange.eci;
|
||||
yoffset += yo;
|
||||
this.contentEl.child(h('div', `${cssPrefix}-canvas-card-wraper`).child(wrap.child(canvas)));
|
||||
}
|
||||
this.el.show();
|
||||
}
|
||||
|
||||
toPrint() {
|
||||
this.el.hide();
|
||||
const { paper } = this;
|
||||
const iframe = h('iframe', '').hide();
|
||||
const { el } = iframe;
|
||||
window.document.body.appendChild(el);
|
||||
const { contentWindow } = el;
|
||||
const idoc = contentWindow.document;
|
||||
const style = document.createElement('style');
|
||||
style.innerHTML = `
|
||||
@page { size: ${paper.width}px ${paper.height}px; };
|
||||
canvas {
|
||||
page-break-before: auto;
|
||||
page-break-after: always;
|
||||
image-rendering: pixelated;
|
||||
};
|
||||
`;
|
||||
idoc.head.appendChild(style);
|
||||
this.canvases.forEach(it => {
|
||||
const cn = it.cloneNode(false);
|
||||
const ctx = cn.getContext('2d');
|
||||
// ctx.imageSmoothingEnabled = true;
|
||||
ctx.drawImage(it, 0, 0);
|
||||
idoc.body.appendChild(cn);
|
||||
});
|
||||
contentWindow.print();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import { h } from './element.js';
|
||||
import { mouseMoveUp } from './event.js';
|
||||
|
||||
export default class Resizer {
|
||||
constructor(vertical = false, minDistance) {
|
||||
this.moving = false;
|
||||
this.vertical = vertical;
|
||||
this.el = h('div', `${cssPrefix}-resizer ${vertical ? 'vertical' : 'horizontal'}`)
|
||||
.children(
|
||||
(this.unhideHoverEl = h('div', `${cssPrefix}-resizer-hover`)
|
||||
.on('dblclick.stop', evt => this.mousedblclickHandler(evt))
|
||||
.css('position', 'absolute')
|
||||
.hide()),
|
||||
(this.hoverEl = h('div', `${cssPrefix}-resizer-hover`).on('mousedown.stop', evt =>
|
||||
this.mousedownHandler(evt),
|
||||
)),
|
||||
(this.lineEl = h('div', `${cssPrefix}-resizer-line`).hide()),
|
||||
)
|
||||
.hide();
|
||||
// cell rect
|
||||
this.cRect = null;
|
||||
this.finishedFn = null;
|
||||
this.minDistance = minDistance;
|
||||
this.unhideFn = () => {
|
||||
console.log('empty function');
|
||||
};
|
||||
}
|
||||
|
||||
showUnhide(index) {
|
||||
this.unhideIndex = index;
|
||||
this.unhideHoverEl.show();
|
||||
}
|
||||
|
||||
hideUnhide() {
|
||||
this.unhideHoverEl.hide();
|
||||
}
|
||||
|
||||
// rect : {top, left, width, height}
|
||||
// line : {width, height}
|
||||
show(rect, line) {
|
||||
const { moving, vertical, hoverEl, lineEl, el, unhideHoverEl } = this;
|
||||
if (moving) return;
|
||||
this.cRect = rect;
|
||||
const { left, top, width, height } = rect;
|
||||
el.offset({
|
||||
left: vertical ? left + width - 5 : left,
|
||||
top: vertical ? top : top + height - 5,
|
||||
}).show();
|
||||
hoverEl.offset({
|
||||
width: vertical ? 5 : width,
|
||||
height: vertical ? height : 5,
|
||||
});
|
||||
lineEl.offset({
|
||||
width: vertical ? 0 : line.width,
|
||||
height: vertical ? line.height : 0,
|
||||
});
|
||||
unhideHoverEl.offset({
|
||||
left: vertical ? 5 - width : left,
|
||||
top: vertical ? top : 5 - height,
|
||||
width: vertical ? 5 : width,
|
||||
height: vertical ? height : 5,
|
||||
});
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.el
|
||||
.offset({
|
||||
left: 0,
|
||||
top: 0,
|
||||
})
|
||||
.hide();
|
||||
this.hideUnhide();
|
||||
}
|
||||
|
||||
mousedblclickHandler() {
|
||||
if (this.unhideIndex) this.unhideFn(this.unhideIndex);
|
||||
}
|
||||
|
||||
mousedownHandler(evt) {
|
||||
let startEvt = evt;
|
||||
const { el, lineEl, cRect, vertical, minDistance } = this;
|
||||
let distance = vertical ? cRect.width : cRect.height;
|
||||
// console.log('distance:', distance);
|
||||
lineEl.show();
|
||||
mouseMoveUp(
|
||||
window,
|
||||
e => {
|
||||
this.moving = true;
|
||||
if (startEvt !== null && e.buttons === 1) {
|
||||
// console.log('top:', top, ', left:', top, ', cRect:', cRect);
|
||||
if (vertical) {
|
||||
distance += e.movementX;
|
||||
if (distance > minDistance) {
|
||||
el.css('left', `${cRect.left + distance}px`);
|
||||
}
|
||||
} else {
|
||||
distance += e.movementY;
|
||||
if (distance > minDistance) {
|
||||
el.css('top', `${cRect.top + distance}px`);
|
||||
}
|
||||
}
|
||||
startEvt = e;
|
||||
}
|
||||
},
|
||||
() => {
|
||||
startEvt = null;
|
||||
lineEl.hide();
|
||||
this.moving = false;
|
||||
this.hide();
|
||||
if (this.finishedFn) {
|
||||
if (distance < minDistance) distance = minDistance;
|
||||
this.finishedFn(cRect, distance);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import { h } from './element.js';
|
||||
|
||||
export default class Scrollbar {
|
||||
constructor(vertical) {
|
||||
this.vertical = vertical;
|
||||
this.moveFn = null;
|
||||
this.el = h('div', `${cssPrefix}-scrollbar ${vertical ? 'vertical' : 'horizontal'}`)
|
||||
.child((this.contentEl = h('div', '')))
|
||||
.on('mousemove.stop', () => {
|
||||
console.log('mousemove.stop');
|
||||
})
|
||||
.on('scroll.stop', evt => {
|
||||
const { scrollTop, scrollLeft } = evt.target;
|
||||
// console.log('scrollTop:', scrollTop);
|
||||
if (this.moveFn) {
|
||||
this.moveFn(this.vertical ? scrollTop : scrollLeft, evt);
|
||||
}
|
||||
// console.log('evt:::', evt);
|
||||
});
|
||||
}
|
||||
|
||||
move(v) {
|
||||
this.el.scroll(v);
|
||||
return this;
|
||||
}
|
||||
|
||||
scroll() {
|
||||
return this.el.scroll();
|
||||
}
|
||||
|
||||
set(distance, contentDistance) {
|
||||
const d = distance - 1;
|
||||
// console.log('distance:', distance, ', contentDistance:', contentDistance);
|
||||
if (contentDistance > d) {
|
||||
const cssKey = this.vertical ? 'height' : 'width';
|
||||
// console.log('d:', d);
|
||||
this.el.css(cssKey, `${d - 15}px`).show();
|
||||
this.contentEl
|
||||
.css(this.vertical ? 'width' : 'height', '1px')
|
||||
.css(cssKey, `${contentDistance}px`);
|
||||
} else {
|
||||
this.el.hide();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,401 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import { CellRange } from '../core/cell_range.js';
|
||||
import { h } from './element.js';
|
||||
|
||||
const selectorHeightBorderWidth = 2 * 2 - 1;
|
||||
let startZIndex = 10;
|
||||
|
||||
class SelectorElement {
|
||||
constructor(useHideInput = false, autoFocus = true) {
|
||||
this.useHideInput = useHideInput;
|
||||
this.autoFocus = autoFocus;
|
||||
this.inputChange = () => {
|
||||
console.log('empty function');
|
||||
};
|
||||
this.cornerEl = h('div', `${cssPrefix}-selector-corner`);
|
||||
this.areaEl = h('div', `${cssPrefix}-selector-area`).child(this.cornerEl).hide();
|
||||
this.clipboardEl = h('div', `${cssPrefix}-selector-clipboard`).hide();
|
||||
this.autofillEl = h('div', `${cssPrefix}-selector-autofill`).hide();
|
||||
this.el = h('div', `${cssPrefix}-selector`)
|
||||
.css('z-index', `${startZIndex}`)
|
||||
.children(this.areaEl, this.clipboardEl, this.autofillEl)
|
||||
.hide();
|
||||
if (useHideInput) {
|
||||
this.hideInput = h('input', '').on('compositionend', evt => {
|
||||
this.inputChange(evt.target.value);
|
||||
});
|
||||
this.el.child((this.hideInputDiv = h('div', 'hide-input').child(this.hideInput)));
|
||||
this.el.child((this.hideInputDiv = h('div', 'hide-input').child(this.hideInput)));
|
||||
}
|
||||
startZIndex += 1;
|
||||
}
|
||||
|
||||
setOffset(v) {
|
||||
this.el.offset(v).show();
|
||||
return this;
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.el.hide();
|
||||
return this;
|
||||
}
|
||||
|
||||
setAreaOffset(v) {
|
||||
const { left, top, width, height } = v;
|
||||
const of = {
|
||||
width: width - selectorHeightBorderWidth + 0.8,
|
||||
height: height - selectorHeightBorderWidth + 0.8,
|
||||
left: left - 0.8,
|
||||
top: top - 0.8,
|
||||
};
|
||||
this.areaEl.offset(of).show();
|
||||
if (this.useHideInput) {
|
||||
this.hideInputDiv.offset(of);
|
||||
if (this.autoFocus) {
|
||||
this.hideInput.val('').focus();
|
||||
} else {
|
||||
this.hideInput.val('');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setClipboardOffset(v) {
|
||||
const { left, top, width, height } = v;
|
||||
this.clipboardEl.offset({
|
||||
left,
|
||||
top,
|
||||
width: width - 5,
|
||||
height: height - 5,
|
||||
});
|
||||
}
|
||||
|
||||
showAutofill(v) {
|
||||
const { left, top, width, height } = v;
|
||||
this.autofillEl
|
||||
.offset({
|
||||
width: width - selectorHeightBorderWidth,
|
||||
height: height - selectorHeightBorderWidth,
|
||||
left,
|
||||
top,
|
||||
})
|
||||
.show();
|
||||
}
|
||||
|
||||
hideAutofill() {
|
||||
this.autofillEl.hide();
|
||||
}
|
||||
|
||||
showClipboard() {
|
||||
this.clipboardEl.show();
|
||||
}
|
||||
|
||||
hideClipboard() {
|
||||
this.clipboardEl.hide();
|
||||
}
|
||||
}
|
||||
|
||||
function calBRAreaOffset(offset) {
|
||||
const { data } = this;
|
||||
const { left, top, width, height, scroll, l, t } = offset;
|
||||
const ftwidth = data.freezeTotalWidth();
|
||||
const ftheight = data.freezeTotalHeight();
|
||||
let left0 = left - ftwidth;
|
||||
if (ftwidth > l) left0 -= scroll.x;
|
||||
let top0 = top - ftheight;
|
||||
if (ftheight > t) top0 -= scroll.y;
|
||||
return {
|
||||
left: left0,
|
||||
top: top0,
|
||||
width,
|
||||
height,
|
||||
};
|
||||
}
|
||||
|
||||
function calTAreaOffset(offset) {
|
||||
const { data } = this;
|
||||
const { left, width, height, l, t, scroll } = offset;
|
||||
const ftwidth = data.freezeTotalWidth();
|
||||
let left0 = left - ftwidth;
|
||||
if (ftwidth > l) left0 -= scroll.x;
|
||||
return {
|
||||
left: left0,
|
||||
top: t,
|
||||
width,
|
||||
height,
|
||||
};
|
||||
}
|
||||
|
||||
function calLAreaOffset(offset) {
|
||||
const { data } = this;
|
||||
const { top, width, height, l, t, scroll } = offset;
|
||||
const ftheight = data.freezeTotalHeight();
|
||||
let top0 = top - ftheight;
|
||||
// console.log('ftheight:', ftheight, ', t:', t);
|
||||
if (ftheight > t) top0 -= scroll.y;
|
||||
return {
|
||||
left: l,
|
||||
top: top0,
|
||||
width,
|
||||
height,
|
||||
};
|
||||
}
|
||||
|
||||
function setBRAreaOffset(offset) {
|
||||
const { br } = this;
|
||||
br.setAreaOffset(calBRAreaOffset.call(this, offset));
|
||||
}
|
||||
|
||||
function setTLAreaOffset(offset) {
|
||||
const { tl } = this;
|
||||
tl.setAreaOffset(offset);
|
||||
}
|
||||
|
||||
function setTAreaOffset(offset) {
|
||||
const { t } = this;
|
||||
t.setAreaOffset(calTAreaOffset.call(this, offset));
|
||||
}
|
||||
|
||||
function setLAreaOffset(offset) {
|
||||
const { l } = this;
|
||||
l.setAreaOffset(calLAreaOffset.call(this, offset));
|
||||
}
|
||||
|
||||
function setLClipboardOffset(offset) {
|
||||
const { l } = this;
|
||||
l.setClipboardOffset(calLAreaOffset.call(this, offset));
|
||||
}
|
||||
|
||||
function setBRClipboardOffset(offset) {
|
||||
const { br } = this;
|
||||
br.setClipboardOffset(calBRAreaOffset.call(this, offset));
|
||||
}
|
||||
|
||||
function setTLClipboardOffset(offset) {
|
||||
const { tl } = this;
|
||||
tl.setClipboardOffset(offset);
|
||||
}
|
||||
|
||||
function setTClipboardOffset(offset) {
|
||||
const { t } = this;
|
||||
t.setClipboardOffset(calTAreaOffset.call(this, offset));
|
||||
}
|
||||
|
||||
function setAllAreaOffset(offset) {
|
||||
setBRAreaOffset.call(this, offset);
|
||||
setTLAreaOffset.call(this, offset);
|
||||
setTAreaOffset.call(this, offset);
|
||||
setLAreaOffset.call(this, offset);
|
||||
}
|
||||
|
||||
function setAllClipboardOffset(offset) {
|
||||
setBRClipboardOffset.call(this, offset);
|
||||
setTLClipboardOffset.call(this, offset);
|
||||
setTClipboardOffset.call(this, offset);
|
||||
setLClipboardOffset.call(this, offset);
|
||||
}
|
||||
|
||||
export default class Selector {
|
||||
constructor(data) {
|
||||
const { autoFocus } = data.settings;
|
||||
this.inputChange = () => {
|
||||
console.log('empty function');
|
||||
};
|
||||
this.data = data;
|
||||
this.br = new SelectorElement(true, autoFocus);
|
||||
this.t = new SelectorElement();
|
||||
this.l = new SelectorElement();
|
||||
this.tl = new SelectorElement();
|
||||
this.br.inputChange = v => {
|
||||
this.inputChange(v);
|
||||
};
|
||||
this.br.el.show();
|
||||
this.offset = null;
|
||||
this.areaOffset = null;
|
||||
this.indexes = null;
|
||||
this.range = null;
|
||||
this.arange = null;
|
||||
this.el = h('div', `${cssPrefix}-selectors`)
|
||||
.children(this.tl.el, this.t.el, this.l.el, this.br.el)
|
||||
.hide();
|
||||
|
||||
// for performance
|
||||
this.lastri = -1;
|
||||
this.lastci = -1;
|
||||
|
||||
startZIndex += 1;
|
||||
}
|
||||
|
||||
resetData(data) {
|
||||
this.data = data;
|
||||
this.range = data.selector.range;
|
||||
this.resetAreaOffset();
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.el.hide();
|
||||
}
|
||||
|
||||
resetOffset() {
|
||||
const { data, tl, t, l, br } = this;
|
||||
const freezeHeight = data.freezeTotalHeight();
|
||||
const freezeWidth = data.freezeTotalWidth();
|
||||
if (freezeHeight > 0 || freezeWidth > 0) {
|
||||
tl.setOffset({ width: freezeWidth, height: freezeHeight });
|
||||
t.setOffset({ left: freezeWidth, height: freezeHeight });
|
||||
l.setOffset({ top: freezeHeight, width: freezeWidth });
|
||||
br.setOffset({ left: freezeWidth, top: freezeHeight });
|
||||
} else {
|
||||
tl.hide();
|
||||
t.hide();
|
||||
l.hide();
|
||||
br.setOffset({ left: 0, top: 0 });
|
||||
}
|
||||
}
|
||||
|
||||
resetAreaOffset() {
|
||||
// console.log('offset:', offset);
|
||||
const offset = this.data.getSelectedRect();
|
||||
const coffset = this.data.getClipboardRect();
|
||||
setAllAreaOffset.call(this, offset);
|
||||
setAllClipboardOffset.call(this, coffset);
|
||||
this.resetOffset();
|
||||
}
|
||||
|
||||
resetBRTAreaOffset() {
|
||||
const offset = this.data.getSelectedRect();
|
||||
const coffset = this.data.getClipboardRect();
|
||||
setBRAreaOffset.call(this, offset);
|
||||
setTAreaOffset.call(this, offset);
|
||||
setBRClipboardOffset.call(this, coffset);
|
||||
setTClipboardOffset.call(this, coffset);
|
||||
this.resetOffset();
|
||||
}
|
||||
|
||||
resetBRLAreaOffset() {
|
||||
const offset = this.data.getSelectedRect();
|
||||
const coffset = this.data.getClipboardRect();
|
||||
setBRAreaOffset.call(this, offset);
|
||||
setLAreaOffset.call(this, offset);
|
||||
setBRClipboardOffset.call(this, coffset);
|
||||
setLClipboardOffset.call(this, coffset);
|
||||
this.resetOffset();
|
||||
}
|
||||
|
||||
set(ri, ci, indexesUpdated = true) {
|
||||
const { data } = this;
|
||||
const cellRange = data.calSelectedRangeByStart(ri, ci);
|
||||
const { sri, sci } = cellRange;
|
||||
if (indexesUpdated) {
|
||||
let [cri, cci] = [ri, ci];
|
||||
if (ri < 0) cri = 0;
|
||||
if (ci < 0) cci = 0;
|
||||
data.selector.setIndexes(cri, cci);
|
||||
this.indexes = [cri, cci];
|
||||
}
|
||||
|
||||
this.moveIndexes = [sri, sci];
|
||||
// this.sIndexes = sIndexes;
|
||||
// this.eIndexes = eIndexes;
|
||||
this.range = cellRange;
|
||||
this.resetAreaOffset();
|
||||
this.el.show();
|
||||
}
|
||||
|
||||
setEnd(ri, ci, moving = true) {
|
||||
const { data, lastri, lastci } = this;
|
||||
if (moving) {
|
||||
if (ri === lastri && ci === lastci) return;
|
||||
this.lastri = ri;
|
||||
this.lastci = ci;
|
||||
}
|
||||
this.range = data.calSelectedRangeByEnd(ri, ci);
|
||||
setAllAreaOffset.call(this, this.data.getSelectedRect());
|
||||
}
|
||||
|
||||
reset() {
|
||||
// console.log('::::', this.data);
|
||||
const { eri, eci } = this.data.selector.range;
|
||||
this.setEnd(eri, eci);
|
||||
}
|
||||
|
||||
showAutofill(ri, ci) {
|
||||
if (ri === -1 && ci === -1) return;
|
||||
// console.log('ri:', ri, ', ci:', ci);
|
||||
// const [sri, sci] = this.sIndexes;
|
||||
// const [eri, eci] = this.eIndexes;
|
||||
const { sri, sci, eri, eci } = this.range;
|
||||
const [nri, nci] = [ri, ci];
|
||||
// const rn = eri - sri;
|
||||
// const cn = eci - sci;
|
||||
const srn = sri - ri;
|
||||
const scn = sci - ci;
|
||||
const ern = eri - ri;
|
||||
const ecn = eci - ci;
|
||||
if (scn > 0) {
|
||||
// left
|
||||
// console.log('left');
|
||||
this.arange = new CellRange(sri, nci, eri, sci - 1);
|
||||
// this.saIndexes = [sri, nci];
|
||||
// this.eaIndexes = [eri, sci - 1];
|
||||
// data.calRangeIndexes2(
|
||||
} else if (srn > 0) {
|
||||
// top
|
||||
// console.log('top');
|
||||
// nri = sri;
|
||||
this.arange = new CellRange(nri, sci, sri - 1, eci);
|
||||
// this.saIndexes = [nri, sci];
|
||||
// this.eaIndexes = [sri - 1, eci];
|
||||
} else if (ecn < 0) {
|
||||
// right
|
||||
// console.log('right');
|
||||
// nci = eci;
|
||||
this.arange = new CellRange(sri, eci + 1, eri, nci);
|
||||
// this.saIndexes = [sri, eci + 1];
|
||||
// this.eaIndexes = [eri, nci];
|
||||
} else if (ern < 0) {
|
||||
// bottom
|
||||
// console.log('bottom');
|
||||
// nri = eri;
|
||||
this.arange = new CellRange(eri + 1, sci, nri, eci);
|
||||
// this.saIndexes = [eri + 1, sci];
|
||||
// this.eaIndexes = [nri, eci];
|
||||
} else {
|
||||
// console.log('else:');
|
||||
this.arange = null;
|
||||
// this.saIndexes = null;
|
||||
// this.eaIndexes = null;
|
||||
return;
|
||||
}
|
||||
if (this.arange !== null) {
|
||||
// console.log(this.saIndexes, ':', this.eaIndexes);
|
||||
const offset = this.data.getRect(this.arange);
|
||||
offset.width += 2;
|
||||
offset.height += 2;
|
||||
const { br, l, t, tl } = this;
|
||||
br.showAutofill(calBRAreaOffset.call(this, offset));
|
||||
l.showAutofill(calLAreaOffset.call(this, offset));
|
||||
t.showAutofill(calTAreaOffset.call(this, offset));
|
||||
tl.showAutofill(offset);
|
||||
}
|
||||
}
|
||||
|
||||
hideAutofill() {
|
||||
['br', 'l', 't', 'tl'].forEach(property => {
|
||||
this[property].hideAutofill();
|
||||
});
|
||||
}
|
||||
|
||||
showClipboard() {
|
||||
const coffset = this.data.getClipboardRect();
|
||||
setAllClipboardOffset.call(this, coffset);
|
||||
['br', 'l', 't', 'tl'].forEach(property => {
|
||||
this[property].showClipboard();
|
||||
});
|
||||
}
|
||||
|
||||
hideClipboard() {
|
||||
['br', 'l', 't', 'tl'].forEach(property => {
|
||||
this[property].hideClipboard();
|
||||
});
|
||||
}
|
||||
}
|
||||
1089
OrangeFormsOpen-VUE3/src/components/SpreadSheet/component/sheet.js
Normal file
1089
OrangeFormsOpen-VUE3/src/components/SpreadSheet/component/sheet.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,148 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import { t } from '../locale/locale.js';
|
||||
import { h } from './element.js';
|
||||
import Button from './button.js';
|
||||
import { bindClickoutside, unbindClickoutside } from './event.js';
|
||||
|
||||
function buildMenu(clsName) {
|
||||
return h('div', `${cssPrefix}-item ${clsName}`);
|
||||
}
|
||||
|
||||
function buildSortItem(it) {
|
||||
return buildMenu('state')
|
||||
.child(t(`sort.${it}`))
|
||||
.on('click.stop', () => this.itemClick(it));
|
||||
}
|
||||
|
||||
function buildFilterBody(items) {
|
||||
const { filterbEl, filterValues } = this;
|
||||
filterbEl.html('');
|
||||
const itemKeys = Object.keys(items);
|
||||
itemKeys.forEach((it, index) => {
|
||||
const cnt = items[it];
|
||||
const active = filterValues.includes(it) ? 'checked' : '';
|
||||
filterbEl.child(
|
||||
h('div', `${cssPrefix}-item state ${active}`)
|
||||
.on('click.stop', () => this.filterClick(index, it))
|
||||
.children(it === '' ? t('filter.empty') : it, h('div', 'label').html(`(${cnt})`)),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function resetFilterHeader() {
|
||||
const { filterhEl, filterValues, values } = this;
|
||||
filterhEl.html(`${filterValues.length} / ${values.length}`);
|
||||
filterhEl.checked(filterValues.length === values.length);
|
||||
}
|
||||
|
||||
export default class SortFilter {
|
||||
constructor() {
|
||||
this.filterbEl = h('div', `${cssPrefix}-body`);
|
||||
this.filterhEl = h('div', `${cssPrefix}-header state`).on('click.stop', () =>
|
||||
this.filterClick(0, 'all'),
|
||||
);
|
||||
this.el = h('div', `${cssPrefix}-sort-filter`)
|
||||
.children(
|
||||
(this.sortAscEl = buildSortItem.call(this, 'asc')),
|
||||
(this.sortDescEl = buildSortItem.call(this, 'desc')),
|
||||
buildMenu('divider'),
|
||||
h('div', `${cssPrefix}-filter`).children(this.filterhEl, this.filterbEl),
|
||||
h('div', `${cssPrefix}-buttons`).children(
|
||||
new Button('cancel').on('click', () => this.btnClick('cancel')),
|
||||
new Button('ok', 'primary').on('click', () => this.btnClick('ok')),
|
||||
),
|
||||
)
|
||||
.hide();
|
||||
// this.setFilters(['test1', 'test2', 'text3']);
|
||||
this.ci = null;
|
||||
this.sortDesc = null;
|
||||
this.values = null;
|
||||
this.filterValues = [];
|
||||
}
|
||||
|
||||
btnClick(it) {
|
||||
if (it === 'ok') {
|
||||
const { ci, sort, filterValues } = this;
|
||||
if (this.ok) {
|
||||
this.ok(ci, sort, 'in', filterValues);
|
||||
}
|
||||
}
|
||||
this.hide();
|
||||
}
|
||||
|
||||
itemClick(it) {
|
||||
// console.log('it:', it);
|
||||
this.sort = it;
|
||||
const { sortAscEl, sortDescEl } = this;
|
||||
sortAscEl.checked(it === 'asc');
|
||||
sortDescEl.checked(it === 'desc');
|
||||
}
|
||||
|
||||
filterClick(index, it) {
|
||||
// console.log('index:', index, it);
|
||||
const { filterbEl, filterValues, values } = this;
|
||||
const children = filterbEl.children();
|
||||
if (it === 'all') {
|
||||
if (children.length === filterValues.length) {
|
||||
this.filterValues = [];
|
||||
children.forEach(i => h(i).checked(false));
|
||||
} else {
|
||||
this.filterValues = Array.from(values);
|
||||
children.forEach(i => h(i).checked(true));
|
||||
}
|
||||
} else {
|
||||
const checked = h(children[index]).toggle('checked');
|
||||
if (checked) {
|
||||
filterValues.push(it);
|
||||
} else {
|
||||
filterValues.splice(
|
||||
filterValues.findIndex(i => i === it),
|
||||
1,
|
||||
);
|
||||
}
|
||||
}
|
||||
resetFilterHeader.call(this);
|
||||
}
|
||||
|
||||
// v: autoFilter
|
||||
// items: {value: cnt}
|
||||
// sort { ci, order }
|
||||
set(ci, items, filter, sort) {
|
||||
this.ci = ci;
|
||||
const { sortAscEl, sortDescEl } = this;
|
||||
if (sort !== null) {
|
||||
this.sort = sort.order;
|
||||
sortAscEl.checked(sort.asc());
|
||||
sortDescEl.checked(sort.desc());
|
||||
} else {
|
||||
this.sortDesc = null;
|
||||
sortAscEl.checked(false);
|
||||
sortDescEl.checked(false);
|
||||
}
|
||||
// this.setFilters(items, filter);
|
||||
this.values = Object.keys(items);
|
||||
this.filterValues = filter ? Array.from(filter.value) : Object.keys(items);
|
||||
buildFilterBody.call(this, items, filter);
|
||||
resetFilterHeader.call(this);
|
||||
}
|
||||
|
||||
setOffset(v) {
|
||||
this.el.offset(v).show();
|
||||
let tindex = 1;
|
||||
bindClickoutside(this.el, () => {
|
||||
if (tindex <= 0) {
|
||||
this.hide();
|
||||
}
|
||||
tindex -= 1;
|
||||
});
|
||||
}
|
||||
|
||||
show() {
|
||||
this.el.show();
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.el.hide();
|
||||
unbindClickoutside(this.el);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import { h } from './element.js';
|
||||
import { bindClickoutside, unbindClickoutside } from './event.js';
|
||||
|
||||
function inputMovePrev(evt) {
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
const { filterItems } = this;
|
||||
if (filterItems.length <= 0) return;
|
||||
if (this.itemIndex >= 0) filterItems[this.itemIndex].toggle();
|
||||
this.itemIndex -= 1;
|
||||
if (this.itemIndex < 0) {
|
||||
this.itemIndex = filterItems.length - 1;
|
||||
}
|
||||
filterItems[this.itemIndex].toggle();
|
||||
}
|
||||
|
||||
function inputMoveNext(evt) {
|
||||
evt.stopPropagation();
|
||||
const { filterItems } = this;
|
||||
if (filterItems.length <= 0) return;
|
||||
if (this.itemIndex >= 0) filterItems[this.itemIndex].toggle();
|
||||
this.itemIndex += 1;
|
||||
if (this.itemIndex > filterItems.length - 1) {
|
||||
this.itemIndex = 0;
|
||||
}
|
||||
filterItems[this.itemIndex].toggle();
|
||||
}
|
||||
|
||||
function inputEnter(evt) {
|
||||
evt.preventDefault();
|
||||
const { filterItems } = this;
|
||||
if (filterItems.length <= 0) return;
|
||||
evt.stopPropagation();
|
||||
if (this.itemIndex < 0) this.itemIndex = 0;
|
||||
filterItems[this.itemIndex].el.click();
|
||||
this.hide();
|
||||
}
|
||||
|
||||
function inputKeydownHandler(evt) {
|
||||
const { keyCode } = evt;
|
||||
if (evt.ctrlKey) {
|
||||
evt.stopPropagation();
|
||||
}
|
||||
switch (keyCode) {
|
||||
case 37: // left
|
||||
evt.stopPropagation();
|
||||
break;
|
||||
case 38: // up
|
||||
inputMovePrev.call(this, evt);
|
||||
break;
|
||||
case 39: // right
|
||||
evt.stopPropagation();
|
||||
break;
|
||||
case 40: // down
|
||||
inputMoveNext.call(this, evt);
|
||||
break;
|
||||
case 13: // enter
|
||||
inputEnter.call(this, evt);
|
||||
break;
|
||||
case 9:
|
||||
inputEnter.call(this, evt);
|
||||
break;
|
||||
default:
|
||||
evt.stopPropagation();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
export default class Suggest {
|
||||
constructor(items, itemClick, width = '200px') {
|
||||
this.filterItems = [];
|
||||
this.items = items;
|
||||
this.el = h('div', `${cssPrefix}-suggest`).css('width', width).hide();
|
||||
this.itemClick = itemClick;
|
||||
this.itemIndex = -1;
|
||||
}
|
||||
|
||||
setOffset(v) {
|
||||
this.el.cssRemoveKeys('top', 'bottom').offset(v);
|
||||
}
|
||||
|
||||
hide() {
|
||||
const { el } = this;
|
||||
this.filterItems = [];
|
||||
this.itemIndex = -1;
|
||||
el.hide();
|
||||
unbindClickoutside(this.el.parent());
|
||||
}
|
||||
|
||||
setItems(items) {
|
||||
this.items = items;
|
||||
// this.search('');
|
||||
}
|
||||
|
||||
search(word) {
|
||||
let { items } = this;
|
||||
if (!/^\s*$/.test(word)) {
|
||||
items = items.filter(it => (it.key || it).startsWith(word.toUpperCase()));
|
||||
}
|
||||
items = items.map(it => {
|
||||
let { title } = it;
|
||||
if (title) {
|
||||
if (typeof title === 'function') {
|
||||
title = title();
|
||||
}
|
||||
} else {
|
||||
title = it;
|
||||
}
|
||||
const item = h('div', `${cssPrefix}-item`)
|
||||
.child(title)
|
||||
.on('click.stop', () => {
|
||||
this.itemClick(it);
|
||||
this.hide();
|
||||
});
|
||||
if (it.label) {
|
||||
item.child(h('div', 'label').html(it.label));
|
||||
}
|
||||
return item;
|
||||
});
|
||||
this.filterItems = items;
|
||||
if (items.length <= 0) {
|
||||
return;
|
||||
}
|
||||
const { el } = this;
|
||||
// items[0].toggle();
|
||||
el.html('')
|
||||
.children(...items)
|
||||
.show();
|
||||
bindClickoutside(el.parent(), () => {
|
||||
this.hide();
|
||||
});
|
||||
}
|
||||
|
||||
bindInputEvents(input) {
|
||||
input.on('keydown', evt => inputKeydownHandler.call(this, evt));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,395 @@
|
||||
import { stringAt } from '../core/alphabet.js';
|
||||
import { getFontSizePxByPt } from '../core/font.js';
|
||||
import _cell from '../core/cell.js';
|
||||
import { formulam } from '../core/formula.js';
|
||||
import { formatm } from '../core/format.js';
|
||||
|
||||
import { Draw, DrawBox, thinLineWidth, npx } from '../canvas/draw.js';
|
||||
// gobal var
|
||||
const cellPaddingWidth = 5;
|
||||
const tableFixedHeaderCleanStyle = { fillStyle: '#fff' };
|
||||
const tableGridStyle = {
|
||||
fillStyle: '#fff',
|
||||
lineWidth: thinLineWidth,
|
||||
strokeStyle: '#e6e6e6',
|
||||
};
|
||||
function tableFixedHeaderStyle() {
|
||||
return {
|
||||
textAlign: 'center',
|
||||
textBaseline: 'middle',
|
||||
font: `500 ${npx(12)}px Source Sans Pro`,
|
||||
fillStyle: '#585757',
|
||||
lineWidth: thinLineWidth(),
|
||||
strokeStyle: '#e6e6e6',
|
||||
};
|
||||
}
|
||||
|
||||
function getDrawBox(data, rindex, cindex, yoffset = 0) {
|
||||
const { left, top, width, height } = data.cellRect(rindex, cindex);
|
||||
return new DrawBox(left, top + yoffset, width, height, cellPaddingWidth);
|
||||
}
|
||||
/*
|
||||
function renderCellBorders(bboxes, translateFunc) {
|
||||
const { draw } = this;
|
||||
if (bboxes) {
|
||||
const rset = new Set();
|
||||
// console.log('bboxes:', bboxes);
|
||||
bboxes.forEach(({ ri, ci, box }) => {
|
||||
if (!rset.has(ri)) {
|
||||
rset.add(ri);
|
||||
translateFunc(ri);
|
||||
}
|
||||
draw.strokeBorders(box);
|
||||
});
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
export function renderCell(draw, data, rindex, cindex, yoffset = 0, scroll) {
|
||||
const { sortedRowMap, rows, cols } = data;
|
||||
if (rows.isHide(rindex) || cols.isHide(cindex)) return;
|
||||
let nrindex = rindex;
|
||||
if (sortedRowMap.has(rindex)) {
|
||||
nrindex = sortedRowMap.get(rindex);
|
||||
}
|
||||
|
||||
const cell = data.getCell(nrindex, cindex);
|
||||
if (cell === null) return;
|
||||
let frozen = false;
|
||||
if ('editable' in cell && cell.editable === false) {
|
||||
frozen = true;
|
||||
}
|
||||
|
||||
const style = data.getCellStyleOrDefault(nrindex, cindex);
|
||||
const dbox = getDrawBox(data, rindex, cindex, yoffset);
|
||||
dbox.bgcolor = style.bgcolor;
|
||||
if (style.border !== undefined) {
|
||||
dbox.setBorders(style.border);
|
||||
// bboxes.push({ ri: rindex, ci: cindex, box: dbox });
|
||||
draw.strokeBorders(dbox);
|
||||
}
|
||||
draw.rect(dbox, () => {
|
||||
if (['image'].includes(cell.type) && !cell.hidden) {
|
||||
let celldata = data.rows.getCell(rindex, cindex);
|
||||
// 如果单元格类型是单选框,则添加前缀的圆弧画法
|
||||
// 在这里传递一下行坐标与列坐标的宽度,方便异步加载图片时使用
|
||||
const fixedIndexWidth = cols.indexWidth;
|
||||
const fixedIndexHeight = rows.indexHeight;
|
||||
draw.geometry(cell, dbox, { fixedIndexWidth, fixedIndexHeight }, style, scroll, celldata);
|
||||
}
|
||||
// render text
|
||||
let cellText = '';
|
||||
if (!data.settings.evalPaused) {
|
||||
cellText = _cell.render(cell.text || '', formulam, (y, x) => data.getCellTextOrDefault(x, y));
|
||||
} else {
|
||||
cellText = cell.text || '';
|
||||
}
|
||||
if (style.format) {
|
||||
// console.log(data.formatm, '>>', cell.format);
|
||||
cellText = formatm[style.format].render(cellText);
|
||||
}
|
||||
const font = Object.assign({}, style.font);
|
||||
font.size = getFontSizePxByPt(font.size);
|
||||
// console.log('style:', style);
|
||||
draw.text(
|
||||
cellText,
|
||||
dbox,
|
||||
{
|
||||
align: style.align,
|
||||
valign: style.valign,
|
||||
font,
|
||||
color: style.color,
|
||||
strike: style.strike,
|
||||
underline: style.underline,
|
||||
},
|
||||
style.textwrap,
|
||||
);
|
||||
// error
|
||||
const error = data.validations.getError(rindex, cindex);
|
||||
if (error) {
|
||||
// console.log('error:', rindex, cindex, error);
|
||||
draw.error(dbox);
|
||||
}
|
||||
if (frozen) {
|
||||
draw.frozen(dbox);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function renderAutofilter(viewRange) {
|
||||
const { data, draw } = this;
|
||||
if (viewRange) {
|
||||
const { autoFilter } = data;
|
||||
if (!autoFilter.active()) return;
|
||||
const afRange = autoFilter.hrange();
|
||||
if (viewRange.intersects(afRange)) {
|
||||
afRange.each((ri, ci) => {
|
||||
const dbox = getDrawBox(data, ri, ci);
|
||||
draw.dropdown(dbox);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function renderContent(viewRange, fw, fh, tx, ty, scroll) {
|
||||
const { draw, data } = this;
|
||||
draw.save();
|
||||
draw.translate(fw, fh).translate(tx, ty);
|
||||
|
||||
const { exceptRowSet } = data;
|
||||
// const exceptRows = Array.from(exceptRowSet);
|
||||
const filteredTranslateFunc = ri => {
|
||||
const ret = exceptRowSet.has(ri);
|
||||
if (ret) {
|
||||
const height = data.rows.getHeight(ri);
|
||||
draw.translate(0, -height);
|
||||
}
|
||||
return !ret;
|
||||
};
|
||||
|
||||
const exceptRowTotalHeight = data.exceptRowTotalHeight(viewRange.sri, viewRange.eri);
|
||||
// 1 render cell
|
||||
draw.save();
|
||||
draw.translate(0, -exceptRowTotalHeight);
|
||||
viewRange.each(
|
||||
(ri, ci) => {
|
||||
renderCell(draw, data, ri, ci, 0, scroll);
|
||||
},
|
||||
ri => filteredTranslateFunc(ri),
|
||||
);
|
||||
draw.restore();
|
||||
|
||||
// 2 render mergeCell
|
||||
const rset = new Set();
|
||||
draw.save();
|
||||
draw.translate(0, -exceptRowTotalHeight);
|
||||
data.eachMergesInView(viewRange, ({ sri, sci, eri }) => {
|
||||
if (!exceptRowSet.has(sri)) {
|
||||
renderCell(draw, data, sri, sci, 0, scroll);
|
||||
} else if (!rset.has(sri)) {
|
||||
rset.add(sri);
|
||||
const height = data.rows.sumHeight(sri, eri + 1);
|
||||
draw.translate(0, -height);
|
||||
}
|
||||
});
|
||||
draw.restore();
|
||||
|
||||
// 3 render autofilter
|
||||
renderAutofilter.call(this, viewRange);
|
||||
|
||||
draw.restore();
|
||||
}
|
||||
|
||||
function renderSelectedHeaderCell(x, y, w, h) {
|
||||
const { draw } = this;
|
||||
draw.save();
|
||||
draw.attr({ fillStyle: 'rgba(76,76,76,.1)' }).fillRect(x, y, w, h);
|
||||
draw.restore();
|
||||
}
|
||||
function renderLeftHeaderCell(x, y, w, h, op) {
|
||||
const { draw } = this;
|
||||
op = op || {};
|
||||
draw.save();
|
||||
draw.attr({ fillStyle: 'rgba(76,76,76,.1)', ...op }).fillRect(x, y, w, h);
|
||||
draw.restore();
|
||||
}
|
||||
// viewRange
|
||||
// type: all | left | top
|
||||
// w: the fixed width of header
|
||||
// h: the fixed height of header
|
||||
// tx: moving distance on x-axis
|
||||
// ty: moving distance on y-axis
|
||||
function renderFixedHeaders(type, viewRange, w, h, tx, ty) {
|
||||
const { draw, data } = this;
|
||||
const sumHeight = viewRange.h; // rows.sumHeight(viewRange.sri, viewRange.eri + 1);
|
||||
const sumWidth = viewRange.w; // cols.sumWidth(viewRange.sci, viewRange.eci + 1);
|
||||
const nty = ty + h;
|
||||
const ntx = tx + w;
|
||||
|
||||
draw.save();
|
||||
// draw rect background
|
||||
draw.attr(tableFixedHeaderCleanStyle);
|
||||
if (type === 'all' || type === 'left') draw.fillRect(0, nty, w, sumHeight);
|
||||
if (type === 'all' || type === 'top') draw.fillRect(ntx, 0, sumWidth, h);
|
||||
|
||||
const { sri, sci, eri, eci } = data.selector.range;
|
||||
// console.log(data.selectIndexes);
|
||||
// draw text
|
||||
// text font, align...
|
||||
draw.attr(tableFixedHeaderStyle());
|
||||
// y-header-text
|
||||
if (type === 'all' || type === 'left') {
|
||||
data.rowEach(viewRange.sri, viewRange.eri, (i, y1, rowHeight) => {
|
||||
const y = nty + y1;
|
||||
const ii = i;
|
||||
draw.line([0, y], [w, y]);
|
||||
if (
|
||||
data.settings.leftFixHeaderRender &&
|
||||
data.settings.leftFixHeaderRender instanceof Function
|
||||
) {
|
||||
let cfg = data.settings.leftFixHeaderRender(i);
|
||||
if (cfg) {
|
||||
renderLeftHeaderCell.call(this, 0, y, w, rowHeight, cfg);
|
||||
} else {
|
||||
if (sri <= ii && ii < eri + 1) {
|
||||
renderSelectedHeaderCell.call(this, 0, y, w, rowHeight);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (sri <= ii && ii < eri + 1) {
|
||||
renderSelectedHeaderCell.call(this, 0, y, w, rowHeight);
|
||||
}
|
||||
}
|
||||
draw.fillText(ii + 1, w / 2, y + rowHeight / 2);
|
||||
if (i > 0 && data.rows.isHide(i - 1)) {
|
||||
draw.save();
|
||||
draw.attr({ strokeStyle: '#c6c6c6' });
|
||||
draw.line([5, y + 5], [w - 5, y + 5]);
|
||||
draw.restore();
|
||||
}
|
||||
});
|
||||
draw.line([0, sumHeight + nty], [w, sumHeight + nty]);
|
||||
draw.line([w, nty], [w, sumHeight + nty]);
|
||||
}
|
||||
// x-header-text
|
||||
if (type === 'all' || type === 'top') {
|
||||
data.colEach(viewRange.sci, viewRange.eci, (i, x1, colWidth) => {
|
||||
const x = ntx + x1;
|
||||
const ii = i;
|
||||
draw.line([x, 0], [x, h]);
|
||||
if (sci <= ii && ii < eci + 1) {
|
||||
renderSelectedHeaderCell.call(this, x, 0, colWidth, h);
|
||||
}
|
||||
draw.fillText(stringAt(ii), x + colWidth / 2, h / 2);
|
||||
if (i > 0 && data.cols.isHide(i - 1)) {
|
||||
draw.save();
|
||||
draw.attr({ strokeStyle: '#c6c6c6' });
|
||||
draw.line([x + 5, 5], [x + 5, h - 5]);
|
||||
draw.restore();
|
||||
}
|
||||
});
|
||||
draw.line([sumWidth + ntx, 0], [sumWidth + ntx, h]);
|
||||
draw.line([0, h], [sumWidth + ntx, h]);
|
||||
}
|
||||
draw.restore();
|
||||
}
|
||||
|
||||
function renderFixedLeftTopCell(fw, fh) {
|
||||
const { draw, data } = this;
|
||||
if (data.settings.mode !== 'edit') return;
|
||||
draw.save();
|
||||
// left-top-cell
|
||||
draw.attr({ fillStyle: '#fff' }).fillRect(0, 0, fw, fh);
|
||||
draw.restore();
|
||||
}
|
||||
|
||||
function renderContentGrid({ sri, sci, eri, eci, w, h }, fw, fh, tx, ty) {
|
||||
const { draw, data } = this;
|
||||
const { settings } = data;
|
||||
|
||||
draw.save();
|
||||
draw.attr(tableGridStyle).translate(fw + tx, fh + ty);
|
||||
// const sumWidth = cols.sumWidth(sci, eci + 1);
|
||||
// const sumHeight = rows.sumHeight(sri, eri + 1);
|
||||
// console.log('sumWidth:', sumWidth);
|
||||
// draw.clearRect(0, 0, w, h);
|
||||
if (!settings.showGrid) {
|
||||
draw.restore();
|
||||
return;
|
||||
}
|
||||
// console.log('rowStart:', rowStart, ', rowLen:', rowLen);
|
||||
data.rowEach(sri, eri, (i, y, ch) => {
|
||||
// console.log('y:', y);
|
||||
if (i !== sri) draw.line([0, y], [w, y]);
|
||||
if (i === eri) draw.line([0, y + ch], [w, y + ch]);
|
||||
});
|
||||
data.colEach(sci, eci, (i, x, cw) => {
|
||||
if (i !== sci) draw.line([x, 0], [x, h]);
|
||||
if (i === eci) draw.line([x + cw, 0], [x + cw, h]);
|
||||
});
|
||||
draw.restore();
|
||||
}
|
||||
|
||||
function renderFreezeHighlightLine(fw, fh, ftw, fth) {
|
||||
const { draw, data } = this;
|
||||
const twidth = data.viewWidth() - fw;
|
||||
const theight = data.viewHeight() - fh;
|
||||
draw.save().translate(fw, fh).attr({ strokeStyle: 'rgba(75, 137, 255, .6)' });
|
||||
draw.line([0, fth], [twidth, fth]);
|
||||
draw.line([ftw, 0], [ftw, theight]);
|
||||
draw.restore();
|
||||
}
|
||||
|
||||
/** end */
|
||||
class Table {
|
||||
constructor(el, data) {
|
||||
this.el = el;
|
||||
this.draw = new Draw(el, data.viewWidth(), data.viewHeight());
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
resetData(data) {
|
||||
this.data = data;
|
||||
this.render();
|
||||
}
|
||||
|
||||
render() {
|
||||
// resize canvas
|
||||
const { data } = this;
|
||||
const { rows, cols } = data;
|
||||
// fixed width of header
|
||||
const fw = cols.indexWidth;
|
||||
// fixed height of header
|
||||
const fh = rows.height;
|
||||
|
||||
this.draw.resize(data.viewWidth(), data.viewHeight());
|
||||
this.clear();
|
||||
|
||||
const viewRange = data.viewRange();
|
||||
// renderAll.call(this, viewRange, data.scroll);
|
||||
const tx = data.freezeTotalWidth();
|
||||
const ty = data.freezeTotalHeight();
|
||||
const { x, y } = data.scroll;
|
||||
// 1
|
||||
renderContentGrid.call(this, viewRange, fw, fh, tx, ty);
|
||||
renderContent.call(this, viewRange, fw, fh, -x, -y, data);
|
||||
renderFixedHeaders.call(this, 'all', viewRange, fw, fh, tx, ty);
|
||||
renderFixedLeftTopCell.call(this, fw, fh);
|
||||
const [fri, fci] = data.freeze;
|
||||
if (fri > 0 || fci > 0) {
|
||||
// 2
|
||||
if (fri > 0) {
|
||||
const vr = viewRange.clone();
|
||||
vr.sri = 0;
|
||||
vr.eri = fri - 1;
|
||||
vr.h = ty;
|
||||
renderContentGrid.call(this, vr, fw, fh, tx, 0);
|
||||
renderContent.call(this, vr, fw, fh, -x, 0, data);
|
||||
renderFixedHeaders.call(this, 'top', vr, fw, fh, tx, 0);
|
||||
}
|
||||
// 3
|
||||
if (fci > 0) {
|
||||
const vr = viewRange.clone();
|
||||
vr.sci = 0;
|
||||
vr.eci = fci - 1;
|
||||
vr.w = tx;
|
||||
renderContentGrid.call(this, vr, fw, fh, 0, ty);
|
||||
renderFixedHeaders.call(this, 'left', vr, fw, fh, 0, ty);
|
||||
renderContent.call(this, vr, fw, fh, 0, -y, data);
|
||||
}
|
||||
// 4
|
||||
const freezeViewRange = data.freezeViewRange();
|
||||
renderContentGrid.call(this, freezeViewRange, fw, fh, 0, 0);
|
||||
renderFixedHeaders.call(this, 'all', freezeViewRange, fw, fh, 0, 0);
|
||||
renderContent.call(this, freezeViewRange, fw, fh, 0, 0, data);
|
||||
// 5
|
||||
renderFreezeHighlightLine.call(this, fw, fh, tx, ty);
|
||||
}
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.draw.clear();
|
||||
}
|
||||
}
|
||||
|
||||
export default Table;
|
||||
@@ -0,0 +1,258 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import { t } from '../locale/locale.js';
|
||||
import { h } from './element.js';
|
||||
import { bind } from './event.js';
|
||||
import tooltip from './tooltip.js';
|
||||
import DropdownFont from './dropdown_font.js';
|
||||
import DropdownFontSize from './dropdown_fontsize.js';
|
||||
import DropdownFormat from './dropdown_format.js';
|
||||
import DropdownFormula from './dropdown_formula.js';
|
||||
import DropdownColor from './dropdown_color.js';
|
||||
import DropdownAlign from './dropdown_align.js';
|
||||
import DropdownBorder from './dropdown_border.js';
|
||||
import Dropdown from './dropdown.js';
|
||||
import Icon from './icon.js';
|
||||
|
||||
function buildIcon(name) {
|
||||
return new Icon(name);
|
||||
}
|
||||
|
||||
function buildButton(tooltipdata) {
|
||||
return h('div', `${cssPrefix}-toolbar-btn`)
|
||||
.on('mouseenter', evt => {
|
||||
tooltip(tooltipdata, evt.target);
|
||||
})
|
||||
.attr('data-tooltip', tooltipdata);
|
||||
}
|
||||
|
||||
function buildDivider() {
|
||||
return h('div', `${cssPrefix}-toolbar-divider`);
|
||||
}
|
||||
|
||||
function buildButtonWithIcon(
|
||||
tooltipdata,
|
||||
iconName,
|
||||
change = () => {
|
||||
console.log('empty function');
|
||||
},
|
||||
) {
|
||||
return buildButton(tooltipdata)
|
||||
.child(buildIcon(iconName))
|
||||
.on('click', () => change());
|
||||
}
|
||||
|
||||
function bindDropdownChange() {
|
||||
this.ddFormat.change = it => this.change('format', it.key);
|
||||
this.ddFont.change = it => this.change('font-name', it.key);
|
||||
this.ddFormula.change = it => this.change('formula', it.key);
|
||||
this.ddFontSize.change = it => this.change('font-size', it.pt);
|
||||
this.ddTextColor.change = it => this.change('color', it);
|
||||
this.ddFillColor.change = it => this.change('bgcolor', it);
|
||||
this.ddAlign.change = it => this.change('align', it);
|
||||
this.ddVAlign.change = it => this.change('valign', it);
|
||||
this.ddBorder.change = it => this.change('border', it);
|
||||
}
|
||||
|
||||
function toggleChange(type) {
|
||||
let elName = type;
|
||||
const types = type.split('-');
|
||||
if (types.length > 1) {
|
||||
types.forEach((it, i) => {
|
||||
if (i === 0) elName = it;
|
||||
else elName += it[0].toUpperCase() + it.substring(1);
|
||||
});
|
||||
}
|
||||
const el = this[`${elName}El`];
|
||||
el.toggle();
|
||||
this.change(type, el.hasClass('active'));
|
||||
}
|
||||
|
||||
class DropdownMore extends Dropdown {
|
||||
constructor() {
|
||||
const icon = new Icon('ellipsis');
|
||||
const moreBtns = h('div', `${cssPrefix}-toolbar-more`);
|
||||
super(icon, 'auto', false, 'bottom-right', moreBtns);
|
||||
this.moreBtns = moreBtns;
|
||||
this.contentEl.css('max-width', '420px');
|
||||
}
|
||||
}
|
||||
|
||||
function initBtns2() {
|
||||
this.btns2 = this.btnChildren.map(it => {
|
||||
const rect = it.box();
|
||||
const { marginLeft, marginRight } = it.computedStyle();
|
||||
return [it, rect.width + parseInt(marginLeft, 10) + parseInt(marginRight, 10)];
|
||||
});
|
||||
}
|
||||
|
||||
function moreResize() {
|
||||
const { el, btns, moreEl, ddMore, btns2 } = this;
|
||||
const { moreBtns, contentEl } = ddMore;
|
||||
el.css('width', `${this.widthFn() - 60}px`);
|
||||
const elBox = el.box();
|
||||
|
||||
let sumWidth = 160;
|
||||
let sumWidth2 = 12;
|
||||
const list1 = [];
|
||||
const list2 = [];
|
||||
btns2.forEach(([it, w], index) => {
|
||||
sumWidth += w;
|
||||
if (index === btns2.length - 1 || sumWidth < elBox.width) {
|
||||
list1.push(it);
|
||||
} else {
|
||||
sumWidth2 += w;
|
||||
list2.push(it);
|
||||
}
|
||||
});
|
||||
btns.html('').children(...list1);
|
||||
moreBtns.html('').children(...list2);
|
||||
contentEl.css('width', `${sumWidth2}px`);
|
||||
if (list2.length > 0) {
|
||||
moreEl.show();
|
||||
} else {
|
||||
moreEl.hide();
|
||||
}
|
||||
}
|
||||
|
||||
export default class Toolbar {
|
||||
constructor(data, widthFn, isHide = false) {
|
||||
this.data = data;
|
||||
this.change = () => {
|
||||
console.log('empty function');
|
||||
};
|
||||
this.widthFn = widthFn;
|
||||
const style = data.defaultStyle();
|
||||
// console.log('data:', data);
|
||||
this.ddFormat = new DropdownFormat();
|
||||
this.ddFont = new DropdownFont();
|
||||
this.ddFormula = new DropdownFormula();
|
||||
this.ddFontSize = new DropdownFontSize();
|
||||
this.ddTextColor = new DropdownColor('text-color', style.color);
|
||||
this.ddFillColor = new DropdownColor('fill-color', style.bgcolor);
|
||||
this.ddAlign = new DropdownAlign(['left', 'center', 'right'], style.align);
|
||||
this.ddVAlign = new DropdownAlign(['top', 'middle', 'bottom'], style.valign);
|
||||
this.ddBorder = new DropdownBorder();
|
||||
this.ddMore = new DropdownMore();
|
||||
this.btnChildren = [
|
||||
(this.undoEl = buildButtonWithIcon(`${t('toolbar.undo')} (Ctrl+Z)`, 'undo', () =>
|
||||
this.change('undo'),
|
||||
)),
|
||||
(this.redoEl = buildButtonWithIcon(`${t('toolbar.undo')} (Ctrl+Y)`, 'redo', () =>
|
||||
this.change('redo'),
|
||||
)),
|
||||
// this.printEl = buildButtonWithIcon('Print (Ctrl+P)', 'print', () => this.change('print')),
|
||||
(this.paintformatEl = buildButtonWithIcon(`${t('toolbar.paintformat')}`, 'paintformat', () =>
|
||||
toggleChange.call(this, 'paintformat'),
|
||||
)),
|
||||
(this.clearformatEl = buildButtonWithIcon(`${t('toolbar.clearformat')}`, 'clearformat', () =>
|
||||
this.change('clearformat'),
|
||||
)),
|
||||
buildDivider(),
|
||||
buildButton(`${t('toolbar.format')}`).child(this.ddFormat.el),
|
||||
buildDivider(),
|
||||
buildButton(`${t('toolbar.font')}`).child(this.ddFont.el),
|
||||
buildButton(`${t('toolbar.fontSize')}`).child(this.ddFontSize.el),
|
||||
buildDivider(),
|
||||
(this.fontBoldEl = buildButtonWithIcon(`${t('toolbar.fontBold')} (Ctrl+B)`, 'bold', () =>
|
||||
toggleChange.call(this, 'font-bold'),
|
||||
)),
|
||||
(this.fontItalicEl = buildButtonWithIcon(
|
||||
`${t('toolbar.fontItalic')} (Ctrl+I)`,
|
||||
'italic',
|
||||
() => toggleChange.call(this, 'font-italic'),
|
||||
)),
|
||||
(this.underlineEl = buildButtonWithIcon(
|
||||
`${t('toolbar.underline')} (Ctrl+U)`,
|
||||
'underline',
|
||||
() => toggleChange.call(this, 'underline'),
|
||||
)),
|
||||
(this.strikeEl = buildButtonWithIcon(`${t('toolbar.strike')}`, 'strike', () =>
|
||||
toggleChange.call(this, 'strike'),
|
||||
)),
|
||||
buildButton(`${t('toolbar.textColor')}`).child(this.ddTextColor.el),
|
||||
buildDivider(),
|
||||
buildButton(`${t('toolbar.fillColor')}`).child(this.ddFillColor.el),
|
||||
buildButton(`${t('toolbar.border')}`).child(this.ddBorder.el),
|
||||
(this.mergeEl = buildButtonWithIcon(`${t('toolbar.merge')}`, 'merge', () =>
|
||||
toggleChange.call(this, 'merge'),
|
||||
)),
|
||||
buildDivider(),
|
||||
buildButton(`${t('toolbar.align')}`).child(this.ddAlign.el),
|
||||
buildButton(`${t('toolbar.valign')}`).child(this.ddVAlign.el),
|
||||
(this.textwrapEl = buildButtonWithIcon(`${t('toolbar.textwrap')}`, 'textwrap', () =>
|
||||
toggleChange.call(this, 'textwrap'),
|
||||
)),
|
||||
buildDivider(),
|
||||
// this.linkEl = buildButtonWithIcon('Insert link', 'link'),
|
||||
// this.chartEl = buildButtonWithIcon('Insert chart', 'chart'),
|
||||
(this.freezeEl = buildButtonWithIcon(`${t('toolbar.freeze')}`, 'freeze', () =>
|
||||
toggleChange.call(this, 'freeze'),
|
||||
)),
|
||||
(this.autofilterEl = buildButtonWithIcon(`${t('toolbar.autofilter')}`, 'autofilter', () =>
|
||||
toggleChange.call(this, 'autofilter'),
|
||||
)),
|
||||
buildButton(`${t('toolbar.formula')}`).child(this.ddFormula.el),
|
||||
// buildDivider(),
|
||||
(this.moreEl = buildButton(`${t('toolbar.more')}`)
|
||||
.child(this.ddMore.el)
|
||||
.hide()),
|
||||
];
|
||||
this.el = h('div', `${cssPrefix}-toolbar`);
|
||||
this.btns = h('div', `${cssPrefix}-toolbar-btns`).children(...this.btnChildren);
|
||||
this.el.child(this.btns);
|
||||
if (isHide) this.el.hide();
|
||||
bindDropdownChange.call(this);
|
||||
this.reset();
|
||||
setTimeout(() => {
|
||||
initBtns2.call(this);
|
||||
moreResize.call(this);
|
||||
}, 0);
|
||||
bind(window, 'resize', () => {
|
||||
moreResize.call(this);
|
||||
});
|
||||
}
|
||||
|
||||
paintformatActive() {
|
||||
return this.paintformatEl.hasClass('active');
|
||||
}
|
||||
|
||||
paintformatToggle() {
|
||||
this.paintformatEl.toggle();
|
||||
}
|
||||
|
||||
trigger(type) {
|
||||
toggleChange.call(this, type);
|
||||
}
|
||||
|
||||
reset() {
|
||||
const { data } = this;
|
||||
const style = data.getSelectedCellStyle();
|
||||
const cell = data.getSelectedCell();
|
||||
// console.log('canUndo:', data.canUndo());
|
||||
this.undoEl.disabled(!data.canUndo());
|
||||
this.redoEl.disabled(!data.canRedo());
|
||||
this.mergeEl.active(data.canUnmerge()).disabled(!data.selector.multiple());
|
||||
this.autofilterEl.active(!data.canAutofilter());
|
||||
// this.mergeEl.disabled();
|
||||
// console.log('selectedCell:', style, cell);
|
||||
const { font } = style;
|
||||
this.ddFont.setTitle(font.name);
|
||||
this.ddFontSize.setTitle(font.size);
|
||||
this.fontBoldEl.active(font.bold);
|
||||
this.fontItalicEl.active(font.italic);
|
||||
this.underlineEl.active(style.underline);
|
||||
this.strikeEl.active(style.strike);
|
||||
this.ddTextColor.setTitle(style.color);
|
||||
this.ddFillColor.setTitle(style.bgcolor);
|
||||
this.ddAlign.setTitle(style.align);
|
||||
this.ddVAlign.setTitle(style.valign);
|
||||
this.textwrapEl.active(style.textwrap);
|
||||
// console.log('freeze is Active:', data.freezeIsActive());
|
||||
this.freezeEl.active(data.freezeIsActive());
|
||||
if (cell) {
|
||||
if (cell.format) {
|
||||
this.ddFormat.setTitle(cell.format);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import DropdownAlign from '../dropdown_align.js';
|
||||
import DropdownItem from './dropdown_item.js';
|
||||
|
||||
export default class Align extends DropdownItem {
|
||||
constructor(value) {
|
||||
super('align', '', value);
|
||||
}
|
||||
|
||||
dropdown() {
|
||||
const { value } = this;
|
||||
return new DropdownAlign(['left', 'center', 'right'], value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import ToggleItem from './toggle_item.js';
|
||||
|
||||
export default class Autofilter extends ToggleItem {
|
||||
constructor() {
|
||||
super('autofilter');
|
||||
}
|
||||
|
||||
setState() {
|
||||
console.log('empty function');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import ToggleItem from './toggle_item.js';
|
||||
|
||||
export default class Bold extends ToggleItem {
|
||||
constructor() {
|
||||
super('font-bold', 'Ctrl+B');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import DropdownBorder from '../dropdown_border.js';
|
||||
import DropdownItem from './dropdown_item.js';
|
||||
|
||||
export default class Border extends DropdownItem {
|
||||
constructor() {
|
||||
super('border');
|
||||
}
|
||||
|
||||
dropdown() {
|
||||
return new DropdownBorder();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import IconItem from './icon_item.js';
|
||||
|
||||
export default class Clearformat extends IconItem {
|
||||
constructor() {
|
||||
super('clearformat');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import Item from './item.js';
|
||||
|
||||
export default class DropdownItem extends Item {
|
||||
dropdown() {
|
||||
console.log('empty function');
|
||||
}
|
||||
|
||||
getValue(v) {
|
||||
return v;
|
||||
}
|
||||
|
||||
element() {
|
||||
const { tag } = this;
|
||||
this.dd = this.dropdown();
|
||||
this.dd.change = it => this.change(tag, this.getValue(it));
|
||||
return super.element().child(this.dd);
|
||||
}
|
||||
|
||||
setState(v) {
|
||||
if (v) {
|
||||
this.value = v;
|
||||
this.dd.setTitle(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import DropdownColor from '../dropdown_color.js';
|
||||
import DropdownItem from './dropdown_item.js';
|
||||
|
||||
export default class FillColor extends DropdownItem {
|
||||
constructor(color) {
|
||||
super('bgcolor', undefined, color);
|
||||
}
|
||||
|
||||
dropdown() {
|
||||
const { tag, value } = this;
|
||||
return new DropdownColor(tag, value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import DropdownFont from '../dropdown_font.js';
|
||||
import DropdownItem from './dropdown_item.js';
|
||||
|
||||
export default class Font extends DropdownItem {
|
||||
constructor() {
|
||||
super('font-name');
|
||||
}
|
||||
|
||||
getValue(it) {
|
||||
return it.key;
|
||||
}
|
||||
|
||||
dropdown() {
|
||||
return new DropdownFont();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import DropdownFontsize from '../dropdown_fontsize.js';
|
||||
import DropdownItem from './dropdown_item.js';
|
||||
|
||||
export default class Format extends DropdownItem {
|
||||
constructor() {
|
||||
super('font-size');
|
||||
}
|
||||
|
||||
getValue(it) {
|
||||
return it.pt;
|
||||
}
|
||||
|
||||
dropdown() {
|
||||
return new DropdownFontsize();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import DropdownFormat from '../dropdown_format.js';
|
||||
import DropdownItem from './dropdown_item.js';
|
||||
|
||||
export default class Format extends DropdownItem {
|
||||
constructor() {
|
||||
super('format');
|
||||
}
|
||||
|
||||
getValue(it) {
|
||||
return it.key;
|
||||
}
|
||||
|
||||
dropdown() {
|
||||
return new DropdownFormat();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import DropdownFormula from '../dropdown_formula.js';
|
||||
import DropdownItem from './dropdown_item.js';
|
||||
|
||||
export default class Format extends DropdownItem {
|
||||
constructor() {
|
||||
super('formula');
|
||||
}
|
||||
|
||||
getValue(it) {
|
||||
return it.key;
|
||||
}
|
||||
|
||||
dropdown() {
|
||||
return new DropdownFormula();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import ToggleItem from './toggle_item.js';
|
||||
|
||||
export default class Freeze extends ToggleItem {
|
||||
constructor() {
|
||||
super('freeze');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import Icon from '../icon.js';
|
||||
import Item from './item.js';
|
||||
|
||||
export default class IconItem extends Item {
|
||||
element() {
|
||||
return super
|
||||
.element()
|
||||
.child(new Icon(this.tag))
|
||||
.on('click', () => this.change(this.tag));
|
||||
}
|
||||
|
||||
setState(disabled) {
|
||||
this.el.disabled(disabled);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
import { h } from '../element.js';
|
||||
import { cssPrefix } from '../../config.js';
|
||||
import { bind } from '../event.js';
|
||||
import Align from './align.js';
|
||||
import Valign from './valign.js';
|
||||
// import Autofilter from './autofilter.js';
|
||||
import Bold from './bold.js';
|
||||
import Italic from './italic.js';
|
||||
import Strike from './strike.js';
|
||||
import Underline from './underline.js';
|
||||
import Border from './border.js';
|
||||
import Clearformat from './clearformat.js';
|
||||
import Paintformat from './paintformat.js';
|
||||
import TextColor from './text_color.js';
|
||||
import FillColor from './fill_color.js';
|
||||
import FontSize from './font_size.js';
|
||||
import Font from './font.js';
|
||||
import Format from './format.js';
|
||||
// import Formula from './formula.js';
|
||||
// import Freeze from './freeze';
|
||||
import Merge from './merge.js';
|
||||
import Redo from './redo.js';
|
||||
import Undo from './undo.js';
|
||||
// import Print from './print.js';
|
||||
// import Textwrap from './textwrap';
|
||||
import More from './more.js';
|
||||
import Item from './item.js';
|
||||
|
||||
function buildDivider() {
|
||||
return h('div', `${cssPrefix}-toolbar-divider`);
|
||||
}
|
||||
|
||||
function initBtns2() {
|
||||
this.btns2 = [];
|
||||
this.items.forEach(it => {
|
||||
if (Array.isArray(it)) {
|
||||
it.forEach(({ el }) => {
|
||||
const rect = el.box();
|
||||
const { marginLeft, marginRight } = el.computedStyle();
|
||||
this.btns2.push([el, rect.width + parseInt(marginLeft, 10) + parseInt(marginRight, 10)]);
|
||||
});
|
||||
} else {
|
||||
const rect = it.box();
|
||||
const { marginLeft, marginRight } = it.computedStyle();
|
||||
this.btns2.push([it, rect.width + parseInt(marginLeft, 10) + parseInt(marginRight, 10)]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function moreResize() {
|
||||
const { el, btns, moreEl, btns2 } = this;
|
||||
const { moreBtns, contentEl } = moreEl.dd;
|
||||
el.css('width', `${this.widthFn()}px`);
|
||||
const elBox = el.box();
|
||||
|
||||
let sumWidth = 160;
|
||||
let sumWidth2 = 12;
|
||||
const list1 = [];
|
||||
const list2 = [];
|
||||
btns2.forEach(([it, w], index) => {
|
||||
sumWidth += w;
|
||||
if (index === btns2.length - 1 || sumWidth < elBox.width) {
|
||||
list1.push(it);
|
||||
} else {
|
||||
sumWidth2 += w;
|
||||
list2.push(it);
|
||||
}
|
||||
});
|
||||
btns.html('').children(...list1);
|
||||
moreBtns.html('').children(...list2);
|
||||
contentEl.css('width', `${sumWidth2}px`);
|
||||
if (list2.length > 0) {
|
||||
moreEl.show();
|
||||
} else {
|
||||
moreEl.hide();
|
||||
}
|
||||
}
|
||||
|
||||
function genBtn(it) {
|
||||
const btn = new Item();
|
||||
btn.el.on('click', () => {
|
||||
if (it.onClick) it.onClick(this.data.getData(), this.data);
|
||||
});
|
||||
btn.tip = it.tip || '';
|
||||
|
||||
let { el } = it;
|
||||
|
||||
if (it.icon) {
|
||||
el = h('img').attr('src', it.icon);
|
||||
}
|
||||
|
||||
if (el) {
|
||||
const icon = h('div', `${cssPrefix}-icon`);
|
||||
icon.child(el);
|
||||
btn.el.child(icon);
|
||||
}
|
||||
|
||||
return btn;
|
||||
}
|
||||
|
||||
export default class Toolbar {
|
||||
constructor(data, widthFn, isHide = false) {
|
||||
this.data = data;
|
||||
this.change = () => {
|
||||
console.log('empty function');
|
||||
};
|
||||
this.widthFn = widthFn;
|
||||
this.isHide = isHide;
|
||||
const style = data.defaultStyle();
|
||||
this.items = [
|
||||
[
|
||||
(this.undoEl = new Undo()),
|
||||
(this.redoEl = new Redo()),
|
||||
// new Print(),
|
||||
(this.paintformatEl = new Paintformat()),
|
||||
(this.clearformatEl = new Clearformat()),
|
||||
],
|
||||
buildDivider(),
|
||||
[(this.formatEl = new Format())],
|
||||
buildDivider(),
|
||||
[(this.fontEl = new Font()), (this.fontSizeEl = new FontSize())],
|
||||
buildDivider(),
|
||||
[
|
||||
(this.boldEl = new Bold()),
|
||||
(this.italicEl = new Italic()),
|
||||
(this.underlineEl = new Underline()),
|
||||
(this.strikeEl = new Strike()),
|
||||
(this.textColorEl = new TextColor(style.color)),
|
||||
],
|
||||
buildDivider(),
|
||||
[
|
||||
(this.fillColorEl = new FillColor(style.bgcolor)),
|
||||
(this.borderEl = new Border()),
|
||||
(this.mergeEl = new Merge()),
|
||||
],
|
||||
buildDivider(),
|
||||
[
|
||||
(this.alignEl = new Align(style.align)),
|
||||
(this.valignEl = new Valign(style.valign)),
|
||||
// this.textwrapEl = new Textwrap()
|
||||
],
|
||||
// buildDivider(),
|
||||
// [
|
||||
// this.freezeEl = new Freeze(),
|
||||
// this.autofilterEl = new Autofilter(),
|
||||
// this.formulaEl = new Formula()
|
||||
// ]
|
||||
];
|
||||
|
||||
const { extendToolbar = {} } = data.settings;
|
||||
|
||||
if (extendToolbar.left && extendToolbar.left.length > 0) {
|
||||
this.items.unshift(buildDivider());
|
||||
const btns = extendToolbar.left.map(genBtn.bind(this));
|
||||
|
||||
this.items.unshift(btns);
|
||||
}
|
||||
if (extendToolbar.right && extendToolbar.right.length > 0) {
|
||||
this.items.push(buildDivider());
|
||||
const btns = extendToolbar.right.map(genBtn.bind(this));
|
||||
this.items.push(btns);
|
||||
}
|
||||
|
||||
this.items.push([(this.moreEl = new More())]);
|
||||
|
||||
this.el = h('div', `${cssPrefix}-toolbar`);
|
||||
this.btns = h('div', `${cssPrefix}-toolbar-btns`);
|
||||
|
||||
this.items.forEach(it => {
|
||||
if (Array.isArray(it)) {
|
||||
it.forEach(i => {
|
||||
this.btns.child(i.el);
|
||||
i.change = (...args) => {
|
||||
this.change(...args);
|
||||
};
|
||||
});
|
||||
} else {
|
||||
this.btns.child(it.el);
|
||||
}
|
||||
});
|
||||
|
||||
this.el.child(this.btns);
|
||||
if (isHide) {
|
||||
this.el.hide();
|
||||
} else {
|
||||
this.reset();
|
||||
setTimeout(() => {
|
||||
initBtns2.call(this);
|
||||
moreResize.call(this);
|
||||
}, 0);
|
||||
bind(window, 'resize', () => {
|
||||
moreResize.call(this);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
paintformatActive() {
|
||||
return this.paintformatEl.active();
|
||||
}
|
||||
|
||||
paintformatToggle() {
|
||||
this.paintformatEl.toggle();
|
||||
}
|
||||
|
||||
trigger(type) {
|
||||
this[`${type}El`].click();
|
||||
}
|
||||
|
||||
resetData(data) {
|
||||
this.data = data;
|
||||
this.reset();
|
||||
}
|
||||
|
||||
reset() {
|
||||
if (this.isHide) return;
|
||||
const { data } = this;
|
||||
const style = data.getSelectedCellStyle();
|
||||
// console.log('canUndo:', data.canUndo());
|
||||
this.undoEl.setState(!data.canUndo());
|
||||
this.redoEl.setState(!data.canRedo());
|
||||
this.mergeEl.setState(data.canUnmerge(), !data.selector.multiple());
|
||||
// this.autofilterEl.setState(!data.canAutofilter());
|
||||
// this.mergeEl.disabled();
|
||||
// console.log('selectedCell:', style, cell);
|
||||
const { font, format } = style;
|
||||
this.formatEl.setState(format);
|
||||
this.fontEl.setState(font.name);
|
||||
this.fontSizeEl.setState(font.size);
|
||||
this.boldEl.setState(font.bold);
|
||||
this.italicEl.setState(font.italic);
|
||||
this.underlineEl.setState(style.underline);
|
||||
this.strikeEl.setState(style.strike);
|
||||
this.textColorEl.setState(style.color);
|
||||
this.fillColorEl.setState(style.bgcolor);
|
||||
this.alignEl.setState(style.align);
|
||||
this.valignEl.setState(style.valign);
|
||||
// this.textwrapEl.setState(style.textwrap);
|
||||
// console.log('freeze is Active:', data.freezeIsActive());
|
||||
// this.freezeEl.setState(data.freezeIsActive());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import ToggleItem from './toggle_item.js';
|
||||
|
||||
export default class Italic extends ToggleItem {
|
||||
constructor() {
|
||||
super('font-italic', 'Ctrl+I');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { cssPrefix } from '../../config.js';
|
||||
import tooltip from '../tooltip.js';
|
||||
import { h } from '../element.js';
|
||||
import { t } from '../../locale/locale.js';
|
||||
|
||||
export default class Item {
|
||||
// tooltip
|
||||
// tag: the subclass type
|
||||
// shortcut: shortcut key
|
||||
constructor(tag, shortcut, value) {
|
||||
this.tip = '';
|
||||
if (tag) this.tip = t(`toolbar.${tag.replace(/-[a-z]/g, c => c[1].toUpperCase())}`);
|
||||
if (shortcut) this.tip += ` (${shortcut})`;
|
||||
this.tag = tag;
|
||||
this.shortcut = shortcut;
|
||||
this.value = value;
|
||||
this.el = this.element();
|
||||
this.change = () => {
|
||||
console.log('empty function');
|
||||
};
|
||||
}
|
||||
|
||||
element() {
|
||||
const { tip } = this;
|
||||
return h('div', `${cssPrefix}-toolbar-btn`)
|
||||
.on('mouseenter', evt => {
|
||||
if (this.tip) tooltip(this.tip, evt.target);
|
||||
})
|
||||
.attr('data-tooltip', tip);
|
||||
}
|
||||
|
||||
setState() {
|
||||
console.log('empty function');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import ToggleItem from './toggle_item.js';
|
||||
|
||||
export default class Merge extends ToggleItem {
|
||||
constructor() {
|
||||
super('merge');
|
||||
}
|
||||
|
||||
setState(active, disabled) {
|
||||
this.el.active(active).disabled(disabled);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import Dropdown from '../dropdown.js';
|
||||
|
||||
import { cssPrefix } from '../../config.js';
|
||||
import { h } from '../element.js';
|
||||
import Icon from '../icon.js';
|
||||
import DropdownItem from './dropdown_item.js';
|
||||
|
||||
class DropdownMore extends Dropdown {
|
||||
constructor() {
|
||||
const icon = new Icon('ellipsis');
|
||||
const moreBtns = h('div', `${cssPrefix}-toolbar-more`);
|
||||
super(icon, 'auto', false, 'bottom-right', moreBtns);
|
||||
this.moreBtns = moreBtns;
|
||||
this.contentEl.css('max-width', '420px');
|
||||
}
|
||||
}
|
||||
|
||||
export default class More extends DropdownItem {
|
||||
constructor() {
|
||||
super('more');
|
||||
this.el.hide();
|
||||
}
|
||||
|
||||
dropdown() {
|
||||
return new DropdownMore();
|
||||
}
|
||||
|
||||
show() {
|
||||
this.el.show();
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.el.hide();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import ToggleItem from './toggle_item.js';
|
||||
|
||||
export default class Paintformat extends ToggleItem {
|
||||
constructor() {
|
||||
super('paintformat');
|
||||
}
|
||||
|
||||
setState() {
|
||||
console.log('empty function');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import IconItem from './icon_item.js';
|
||||
|
||||
export default class Print extends IconItem {
|
||||
constructor() {
|
||||
super('print', 'Ctrl+P');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import IconItem from './icon_item.js';
|
||||
|
||||
export default class Redo extends IconItem {
|
||||
constructor() {
|
||||
super('redo', 'Ctrl+Y');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import ToggleItem from './toggle_item.js';
|
||||
|
||||
export default class Strike extends ToggleItem {
|
||||
constructor() {
|
||||
super('strike', 'Ctrl+U');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import DropdownColor from '../dropdown_color.js';
|
||||
import DropdownItem from './dropdown_item.js';
|
||||
|
||||
export default class TextColor extends DropdownItem {
|
||||
constructor(color) {
|
||||
super('color', undefined, color);
|
||||
}
|
||||
|
||||
dropdown() {
|
||||
const { tag, value } = this;
|
||||
return new DropdownColor(tag, value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import ToggleItem from './toggle_item.js';
|
||||
|
||||
export default class Textwrap extends ToggleItem {
|
||||
constructor() {
|
||||
super('textwrap');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import Icon from '../icon.js';
|
||||
import Item from './item.js';
|
||||
|
||||
export default class ToggleItem extends Item {
|
||||
element() {
|
||||
const { tag } = this;
|
||||
return super
|
||||
.element()
|
||||
.child(new Icon(tag))
|
||||
.on('click', () => this.click());
|
||||
}
|
||||
|
||||
click() {
|
||||
this.change(this.tag, this.toggle());
|
||||
}
|
||||
|
||||
setState(active) {
|
||||
this.el.active(active);
|
||||
}
|
||||
|
||||
toggle() {
|
||||
return this.el.toggle();
|
||||
}
|
||||
|
||||
active() {
|
||||
return this.el.hasClass('active');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import ToggleItem from './toggle_item.js';
|
||||
|
||||
export default class Underline extends ToggleItem {
|
||||
constructor() {
|
||||
super('underline', 'Ctrl+U');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import IconItem from './icon_item.js';
|
||||
|
||||
export default class Undo extends IconItem {
|
||||
constructor() {
|
||||
super('undo', 'Ctrl+Z');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import DropdownAlign from '../dropdown_align.js';
|
||||
import DropdownItem from './dropdown_item.js';
|
||||
|
||||
export default class Valign extends DropdownItem {
|
||||
constructor(value) {
|
||||
super('valign', '', value);
|
||||
}
|
||||
|
||||
dropdown() {
|
||||
const { value } = this;
|
||||
return new DropdownAlign(['top', 'middle', 'bottom'], value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { cssPrefix } from '../config.js';
|
||||
import { h } from './element.js';
|
||||
import { bind } from './event.js';
|
||||
|
||||
export default function tooltip(html, target) {
|
||||
if (target.classList.contains('active')) {
|
||||
return;
|
||||
}
|
||||
const { left, top, width, height } = target.getBoundingClientRect();
|
||||
const el = h('div', `${cssPrefix}-tooltip`).html(html).show();
|
||||
document.body.appendChild(el.el);
|
||||
const elBox = el.box();
|
||||
// console.log('elBox:', elBox);
|
||||
el.css('left', `${left + width / 2 - elBox.width / 2}px`).css('top', `${top + height + 2}px`);
|
||||
|
||||
bind(target, 'mouseleave', () => {
|
||||
if (document.body.contains(el.el)) {
|
||||
document.body.removeChild(el.el);
|
||||
}
|
||||
});
|
||||
|
||||
bind(target, 'click', () => {
|
||||
if (document.body.contains(el.el)) {
|
||||
document.body.removeChild(el.el);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export const cssPrefix = 'x-spreadsheet';
|
||||
export const dpr = window.devicePixelRatio || 1;
|
||||
export default {
|
||||
cssPrefix,
|
||||
dpr,
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
// font.js
|
||||
/**
|
||||
* @typedef {number} fontsizePX px for fontSize
|
||||
*/
|
||||
/**
|
||||
* @typedef {number} fontsizePT pt for fontSize
|
||||
*/
|
||||
/**
|
||||
* @typedef {object} BaseFont
|
||||
* @property {string} key inner key
|
||||
* @property {string} title title for display
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} FontSize
|
||||
* @property {fontsizePT} pt
|
||||
* @property {fontsizePX} px
|
||||
*/
|
||||
|
||||
// alphabet.js
|
||||
/**
|
||||
* @typedef {string} tagA1 A1 tag for XY-tag (0, 0)
|
||||
* @example "A1"
|
||||
*/
|
||||
/**
|
||||
* @typedef {[number, number]} tagXY
|
||||
* @example [0, 0]
|
||||
*/
|
||||
117
OrangeFormsOpen-VUE3/src/components/SpreadSheet/core/alphabet.js
Normal file
117
OrangeFormsOpen-VUE3/src/components/SpreadSheet/core/alphabet.js
Normal file
@@ -0,0 +1,117 @@
|
||||
import './_.prototypes.js';
|
||||
|
||||
const alphabets = [
|
||||
'A',
|
||||
'B',
|
||||
'C',
|
||||
'D',
|
||||
'E',
|
||||
'F',
|
||||
'G',
|
||||
'H',
|
||||
'I',
|
||||
'J',
|
||||
'K',
|
||||
'L',
|
||||
'M',
|
||||
'N',
|
||||
'O',
|
||||
'P',
|
||||
'Q',
|
||||
'R',
|
||||
'S',
|
||||
'T',
|
||||
'U',
|
||||
'V',
|
||||
'W',
|
||||
'X',
|
||||
'Y',
|
||||
'Z',
|
||||
];
|
||||
|
||||
/** index number 2 letters
|
||||
* @example stringAt(26) ==> 'AA'
|
||||
* @date 2019-10-10
|
||||
* @export
|
||||
* @param {number} index
|
||||
* @returns {string}
|
||||
*/
|
||||
export function stringAt(index) {
|
||||
let str = '';
|
||||
let cindex = index;
|
||||
while (cindex >= alphabets.length) {
|
||||
cindex /= alphabets.length;
|
||||
cindex -= 1;
|
||||
str += alphabets[parseInt(cindex, 10) % alphabets.length];
|
||||
}
|
||||
const last = index % alphabets.length;
|
||||
str += alphabets[last];
|
||||
return str;
|
||||
}
|
||||
|
||||
/** translate letter in A1-tag to number
|
||||
* @date 2019-10-10
|
||||
* @export
|
||||
* @param {string} str "AA" in A1-tag "AA1"
|
||||
* @returns {number}
|
||||
*/
|
||||
export function indexAt(str) {
|
||||
let ret = 0;
|
||||
for (let i = 0; i !== str.length; ++i) ret = 26 * ret + str.charCodeAt(i) - 64;
|
||||
return ret - 1;
|
||||
}
|
||||
|
||||
// B10 => x,y
|
||||
/** translate A1-tag to XY-tag
|
||||
* @date 2019-10-10
|
||||
* @export
|
||||
* @param {tagA1} src
|
||||
* @returns {tagXY}
|
||||
*/
|
||||
export function expr2xy(src) {
|
||||
let x = '';
|
||||
let y = '';
|
||||
for (let i = 0; i < src.length; i += 1) {
|
||||
if (src.charAt(i) >= '0' && src.charAt(i) <= '9') {
|
||||
y += src.charAt(i);
|
||||
} else {
|
||||
x += src.charAt(i);
|
||||
}
|
||||
}
|
||||
return [indexAt(x), parseInt(y, 10) - 1];
|
||||
}
|
||||
|
||||
/** translate XY-tag to A1-tag
|
||||
* @example x,y => B10
|
||||
* @date 2019-10-10
|
||||
* @export
|
||||
* @param {number} x
|
||||
* @param {number} y
|
||||
* @returns {tagA1}
|
||||
*/
|
||||
export function xy2expr(x, y) {
|
||||
return `${stringAt(x)}${y + 1}`;
|
||||
}
|
||||
|
||||
/** translate A1-tag src by (xn, yn)
|
||||
* @date 2019-10-10
|
||||
* @export
|
||||
* @param {tagA1} src
|
||||
* @param {number} xn
|
||||
* @param {number} yn
|
||||
* @returns {tagA1}
|
||||
*/
|
||||
export function expr2expr(src, xn, yn, condition = () => true) {
|
||||
if (xn === 0 && yn === 0) return src;
|
||||
const [x, y] = expr2xy(src);
|
||||
if (!condition(x, y)) return src;
|
||||
return xy2expr(x + xn, y + yn);
|
||||
}
|
||||
|
||||
export default {
|
||||
stringAt,
|
||||
indexAt,
|
||||
expr2xy,
|
||||
xy2expr,
|
||||
expr2expr,
|
||||
};
|
||||
@@ -0,0 +1,183 @@
|
||||
import { CellRange } from './cell_range.js';
|
||||
// operator: all|eq|neq|gt|gte|lt|lte|in|be
|
||||
// value:
|
||||
// in => []
|
||||
// be => [min, max]
|
||||
class Filter {
|
||||
constructor(ci, operator, value) {
|
||||
this.ci = ci;
|
||||
this.operator = operator;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
set(operator, value) {
|
||||
this.operator = operator;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
includes(v) {
|
||||
const { operator, value } = this;
|
||||
if (operator === 'all') {
|
||||
return true;
|
||||
}
|
||||
if (operator === 'in') {
|
||||
return value.includes(v);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
vlength() {
|
||||
const { operator, value } = this;
|
||||
if (operator === 'in') {
|
||||
return value.length;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
getData() {
|
||||
const { ci, operator, value } = this;
|
||||
return { ci, operator, value };
|
||||
}
|
||||
}
|
||||
|
||||
class Sort {
|
||||
constructor(ci, order) {
|
||||
this.ci = ci;
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
asc() {
|
||||
return this.order === 'asc';
|
||||
}
|
||||
|
||||
desc() {
|
||||
return this.order === 'desc';
|
||||
}
|
||||
}
|
||||
|
||||
export default class AutoFilter {
|
||||
constructor() {
|
||||
this.ref = null;
|
||||
this.filters = [];
|
||||
this.sort = null;
|
||||
}
|
||||
|
||||
setData({ ref, filters, sort }) {
|
||||
if (ref != null) {
|
||||
this.ref = ref;
|
||||
this.filters = filters.map(it => new Filter(it.ci, it.operator, it.value));
|
||||
if (sort) {
|
||||
this.sort = new Sort(sort.ci, sort.order);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getData() {
|
||||
if (this.active()) {
|
||||
const { ref, filters, sort } = this;
|
||||
return { ref, filters: filters.map(it => it.getData()), sort };
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
addFilter(ci, operator, value) {
|
||||
const filter = this.getFilter(ci);
|
||||
if (filter == null) {
|
||||
this.filters.push(new Filter(ci, operator, value));
|
||||
} else {
|
||||
filter.set(operator, value);
|
||||
}
|
||||
}
|
||||
|
||||
setSort(ci, order) {
|
||||
this.sort = order ? new Sort(ci, order) : null;
|
||||
}
|
||||
|
||||
includes(ri, ci) {
|
||||
if (this.active()) {
|
||||
return this.hrange().includes(ri, ci);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
getSort(ci) {
|
||||
const { sort } = this;
|
||||
if (sort && sort.ci === ci) {
|
||||
return sort;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
getFilter(ci) {
|
||||
const { filters } = this;
|
||||
for (let i = 0; i < filters.length; i += 1) {
|
||||
if (filters[i].ci === ci) {
|
||||
return filters[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
filteredRows(getCell) {
|
||||
// const ary = [];
|
||||
// let lastri = 0;
|
||||
const rset = new Set();
|
||||
const fset = new Set();
|
||||
if (this.active()) {
|
||||
const { sri, eri } = this.range();
|
||||
const { filters } = this;
|
||||
for (let ri = sri + 1; ri <= eri; ri += 1) {
|
||||
for (let i = 0; i < filters.length; i += 1) {
|
||||
const filter = filters[i];
|
||||
const cell = getCell(ri, filter.ci);
|
||||
const ctext = cell ? cell.text : '';
|
||||
if (!filter.includes(ctext)) {
|
||||
rset.add(ri);
|
||||
break;
|
||||
} else {
|
||||
fset.add(ri);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return { rset, fset };
|
||||
}
|
||||
|
||||
items(ci, getCell) {
|
||||
const m = {};
|
||||
if (this.active()) {
|
||||
const { sri, eri } = this.range();
|
||||
for (let ri = sri + 1; ri <= eri; ri += 1) {
|
||||
const cell = getCell(ri, ci);
|
||||
if (cell !== null && !/^\s*$/.test(cell.text)) {
|
||||
const key = cell.text;
|
||||
const cnt = (m[key] || 0) + 1;
|
||||
m[key] = cnt;
|
||||
} else {
|
||||
m[''] = (m[''] || 0) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
range() {
|
||||
return CellRange.valueOf(this.ref);
|
||||
}
|
||||
|
||||
hrange() {
|
||||
const r = this.range();
|
||||
r.eri = r.sri;
|
||||
return r;
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.ref = null;
|
||||
this.filters = [];
|
||||
this.sort = null;
|
||||
}
|
||||
|
||||
active() {
|
||||
return this.ref !== null;
|
||||
}
|
||||
}
|
||||
224
OrangeFormsOpen-VUE3/src/components/SpreadSheet/core/cell.js
Normal file
224
OrangeFormsOpen-VUE3/src/components/SpreadSheet/core/cell.js
Normal file
@@ -0,0 +1,224 @@
|
||||
import { expr2xy, xy2expr } from './alphabet.js';
|
||||
import { numberCalc } from './helper.js';
|
||||
|
||||
// Converting infix expression to a suffix expression
|
||||
// src: AVERAGE(SUM(A1,A2), B1) + 50 + B20
|
||||
// return: [A1, A2], SUM[, B1],AVERAGE,50,+,B20,+
|
||||
const infixExprToSuffixExpr = src => {
|
||||
const operatorStack = [];
|
||||
const stack = [];
|
||||
let subStrs = []; // SUM, A1, B2, 50 ...
|
||||
let fnArgType = 0; // 1 => , 2 => :
|
||||
let fnArgOperator = '';
|
||||
let fnArgsLen = 1; // A1,A2,A3...
|
||||
let oldc = '';
|
||||
for (let i = 0; i < src.length; i += 1) {
|
||||
const c = src.charAt(i);
|
||||
if (c !== ' ') {
|
||||
if (c >= 'a' && c <= 'z') {
|
||||
subStrs.push(c.toUpperCase());
|
||||
} else if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || c === '.') {
|
||||
subStrs.push(c);
|
||||
} else if (c === '"') {
|
||||
i += 1;
|
||||
while (src.charAt(i) !== '"') {
|
||||
subStrs.push(src.charAt(i));
|
||||
i += 1;
|
||||
}
|
||||
stack.push(`"${subStrs.join('')}`);
|
||||
subStrs = [];
|
||||
} else if (c === '-' && /[+\-*/,(]/.test(oldc)) {
|
||||
subStrs.push(c);
|
||||
} else {
|
||||
// console.log('subStrs:', subStrs.join(''), stack);
|
||||
if (c !== '(' && subStrs.length > 0) {
|
||||
stack.push(subStrs.join(''));
|
||||
}
|
||||
if (c === ')') {
|
||||
let c1 = operatorStack.pop();
|
||||
if (fnArgType === 2) {
|
||||
// fn argument range => A1:B5
|
||||
try {
|
||||
const [ex, ey] = expr2xy(stack.pop());
|
||||
const [sx, sy] = expr2xy(stack.pop());
|
||||
// console.log('::', sx, sy, ex, ey);
|
||||
let rangelen = 0;
|
||||
for (let x = sx; x <= ex; x += 1) {
|
||||
for (let y = sy; y <= ey; y += 1) {
|
||||
stack.push(xy2expr(x, y));
|
||||
rangelen += 1;
|
||||
}
|
||||
}
|
||||
stack.push([c1, rangelen]);
|
||||
} catch (e) {
|
||||
// console.log(e);
|
||||
}
|
||||
} else if (fnArgType === 1 || fnArgType === 3) {
|
||||
if (fnArgType === 3) stack.push(fnArgOperator);
|
||||
// fn argument => A1,A2,B5
|
||||
stack.push([c1, fnArgsLen]);
|
||||
fnArgsLen = 1;
|
||||
} else {
|
||||
// console.log('c1:', c1, fnArgType, stack, operatorStack);
|
||||
while (c1 !== '(') {
|
||||
stack.push(c1);
|
||||
if (operatorStack.length <= 0) break;
|
||||
c1 = operatorStack.pop();
|
||||
}
|
||||
}
|
||||
fnArgType = 0;
|
||||
} else if (c === '=' || c === '>' || c === '<') {
|
||||
const nc = src.charAt(i + 1);
|
||||
fnArgOperator = c;
|
||||
if (nc === '=' || nc === '-') {
|
||||
fnArgOperator += nc;
|
||||
i += 1;
|
||||
}
|
||||
fnArgType = 3;
|
||||
} else if (c === ':') {
|
||||
fnArgType = 2;
|
||||
} else if (c === ',') {
|
||||
if (fnArgType === 3) {
|
||||
stack.push(fnArgOperator);
|
||||
}
|
||||
fnArgType = 1;
|
||||
fnArgsLen += 1;
|
||||
} else if (c === '(' && subStrs.length > 0) {
|
||||
// function
|
||||
operatorStack.push(subStrs.join(''));
|
||||
} else {
|
||||
// priority: */ > +-
|
||||
// console.log('xxxx:', operatorStack, c, stack);
|
||||
if (operatorStack.length > 0 && (c === '+' || c === '-')) {
|
||||
let top = operatorStack[operatorStack.length - 1];
|
||||
if (top !== '(') stack.push(operatorStack.pop());
|
||||
if (top === '*' || top === '/') {
|
||||
while (operatorStack.length > 0) {
|
||||
top = operatorStack[operatorStack.length - 1];
|
||||
if (top !== '(') stack.push(operatorStack.pop());
|
||||
else break;
|
||||
}
|
||||
}
|
||||
} else if (operatorStack.length > 0) {
|
||||
const top = operatorStack[operatorStack.length - 1];
|
||||
if (top === '*' || top === '/') stack.push(operatorStack.pop());
|
||||
}
|
||||
operatorStack.push(c);
|
||||
}
|
||||
subStrs = [];
|
||||
}
|
||||
oldc = c;
|
||||
}
|
||||
}
|
||||
if (subStrs.length > 0) {
|
||||
stack.push(subStrs.join(''));
|
||||
}
|
||||
while (operatorStack.length > 0) {
|
||||
stack.push(operatorStack.pop());
|
||||
}
|
||||
return stack;
|
||||
};
|
||||
|
||||
const evalSubExpr = (subExpr, cellRender) => {
|
||||
const [fl] = subExpr;
|
||||
let expr = subExpr;
|
||||
if (fl === '"') {
|
||||
return subExpr.substring(1);
|
||||
}
|
||||
let ret = 1;
|
||||
if (fl === '-') {
|
||||
expr = subExpr.substring(1);
|
||||
ret = -1;
|
||||
}
|
||||
if (expr[0] >= '0' && expr[0] <= '9') {
|
||||
return ret * Number(expr);
|
||||
}
|
||||
const [x, y] = expr2xy(expr);
|
||||
return ret * cellRender(x, y);
|
||||
};
|
||||
|
||||
// evaluate the suffix expression
|
||||
// srcStack: <= infixExprToSufixExpr
|
||||
// formulaMap: {'SUM': {}, ...}
|
||||
// cellRender: (x, y) => {}
|
||||
const evalSuffixExpr = (srcStack, formulaMap, cellRender, cellList) => {
|
||||
const stack = [];
|
||||
// console.log(':::::formulaMap:', formulaMap);
|
||||
for (let i = 0; i < srcStack.length; i += 1) {
|
||||
// console.log(':::>>>', srcStack[i]);
|
||||
const expr = srcStack[i];
|
||||
const fc = expr[0];
|
||||
if (expr === '+') {
|
||||
const top = stack.pop();
|
||||
stack.push(numberCalc('+', stack.pop(), top));
|
||||
} else if (expr === '-') {
|
||||
if (stack.length === 1) {
|
||||
const top = stack.pop();
|
||||
stack.push(numberCalc('*', top, -1));
|
||||
} else {
|
||||
const top = stack.pop();
|
||||
stack.push(numberCalc('-', stack.pop(), top));
|
||||
}
|
||||
} else if (expr === '*') {
|
||||
stack.push(numberCalc('*', stack.pop(), stack.pop()));
|
||||
} else if (expr === '/') {
|
||||
const top = stack.pop();
|
||||
stack.push(numberCalc('/', stack.pop(), top));
|
||||
} else if (fc === '=' || fc === '>' || fc === '<') {
|
||||
let top = stack.pop();
|
||||
if (!Number.isNaN(top)) top = Number(top);
|
||||
let left = stack.pop();
|
||||
if (!Number.isNaN(left)) left = Number(left);
|
||||
let ret = false;
|
||||
if (fc === '=') {
|
||||
ret = left === top;
|
||||
} else if (expr === '>') {
|
||||
ret = left > top;
|
||||
} else if (expr === '>=') {
|
||||
ret = left >= top;
|
||||
} else if (expr === '<') {
|
||||
ret = left < top;
|
||||
} else if (expr === '<=') {
|
||||
ret = left <= top;
|
||||
}
|
||||
stack.push(ret);
|
||||
} else if (Array.isArray(expr)) {
|
||||
const [formula, len] = expr;
|
||||
const params = [];
|
||||
for (let j = 0; j < len; j += 1) {
|
||||
params.push(stack.pop());
|
||||
}
|
||||
stack.push(formulaMap[formula].render(params.reverse()));
|
||||
} else {
|
||||
if (cellList.includes(expr)) {
|
||||
return 0;
|
||||
}
|
||||
if ((fc >= 'a' && fc <= 'z') || (fc >= 'A' && fc <= 'Z')) {
|
||||
cellList.push(expr);
|
||||
}
|
||||
stack.push(evalSubExpr(expr, cellRender));
|
||||
cellList.pop();
|
||||
}
|
||||
// console.log('stack:', stack);
|
||||
}
|
||||
return stack[0];
|
||||
};
|
||||
|
||||
const cellRender = (src, formulaMap, getCellText, cellList = []) => {
|
||||
if (src[0] === '=') {
|
||||
const stack = infixExprToSuffixExpr(src.substring(1));
|
||||
if (stack.length <= 0) return src;
|
||||
return evalSuffixExpr(
|
||||
stack,
|
||||
formulaMap,
|
||||
(x, y) => cellRender(getCellText(x, y), formulaMap, getCellText, cellList),
|
||||
cellList,
|
||||
);
|
||||
}
|
||||
return src;
|
||||
};
|
||||
|
||||
export default {
|
||||
render: cellRender,
|
||||
};
|
||||
export { infixExprToSuffixExpr };
|
||||
@@ -0,0 +1,214 @@
|
||||
import { xy2expr, expr2xy } from './alphabet.js';
|
||||
|
||||
class CellRange {
|
||||
constructor(sri, sci, eri, eci, w = 0, h = 0) {
|
||||
this.sri = sri;
|
||||
this.sci = sci;
|
||||
this.eri = eri;
|
||||
this.eci = eci;
|
||||
this.w = w;
|
||||
this.h = h;
|
||||
}
|
||||
|
||||
set(sri, sci, eri, eci) {
|
||||
this.sri = sri;
|
||||
this.sci = sci;
|
||||
this.eri = eri;
|
||||
this.eci = eci;
|
||||
}
|
||||
|
||||
multiple() {
|
||||
return this.eri - this.sri > 0 || this.eci - this.sci > 0;
|
||||
}
|
||||
|
||||
// cell-index: ri, ci
|
||||
// cell-ref: A10
|
||||
includes(...args) {
|
||||
let [ri, ci] = [0, 0];
|
||||
if (args.length === 1) {
|
||||
[ci, ri] = expr2xy(args[0]);
|
||||
} else if (args.length === 2) {
|
||||
[ri, ci] = args;
|
||||
}
|
||||
const { sri, sci, eri, eci } = this;
|
||||
return sri <= ri && ri <= eri && sci <= ci && ci <= eci;
|
||||
}
|
||||
|
||||
each(cb, rowFilter = () => true) {
|
||||
const { sri, sci, eri, eci } = this;
|
||||
for (let i = sri; i <= eri; i += 1) {
|
||||
if (rowFilter(i)) {
|
||||
for (let j = sci; j <= eci; j += 1) {
|
||||
cb(i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
contains(other) {
|
||||
return (
|
||||
this.sri <= other.sri &&
|
||||
this.sci <= other.sci &&
|
||||
this.eri >= other.eri &&
|
||||
this.eci >= other.eci
|
||||
);
|
||||
}
|
||||
|
||||
// within
|
||||
within(other) {
|
||||
return (
|
||||
this.sri >= other.sri &&
|
||||
this.sci >= other.sci &&
|
||||
this.eri <= other.eri &&
|
||||
this.eci <= other.eci
|
||||
);
|
||||
}
|
||||
|
||||
// disjoint
|
||||
disjoint(other) {
|
||||
return (
|
||||
this.sri > other.eri || this.sci > other.eci || other.sri > this.eri || other.sci > this.eci
|
||||
);
|
||||
}
|
||||
|
||||
// intersects
|
||||
intersects(other) {
|
||||
return (
|
||||
this.sri <= other.eri &&
|
||||
this.sci <= other.eci &&
|
||||
other.sri <= this.eri &&
|
||||
other.sci <= this.eci
|
||||
);
|
||||
}
|
||||
|
||||
// union
|
||||
union(other) {
|
||||
const { sri, sci, eri, eci } = this;
|
||||
return new CellRange(
|
||||
other.sri < sri ? other.sri : sri,
|
||||
other.sci < sci ? other.sci : sci,
|
||||
other.eri > eri ? other.eri : eri,
|
||||
other.eci > eci ? other.eci : eci,
|
||||
);
|
||||
}
|
||||
|
||||
// intersection
|
||||
// intersection(other) {}
|
||||
|
||||
// Returns Array<CellRange> that represents that part of this that does not intersect with other
|
||||
// difference
|
||||
difference(other) {
|
||||
const ret = [];
|
||||
const addRet = (sri, sci, eri, eci) => {
|
||||
ret.push(new CellRange(sri, sci, eri, eci));
|
||||
};
|
||||
const { sri, sci, eri, eci } = this;
|
||||
const dsr = other.sri - sri;
|
||||
const dsc = other.sci - sci;
|
||||
const der = eri - other.eri;
|
||||
const dec = eci - other.eci;
|
||||
if (dsr > 0) {
|
||||
addRet(sri, sci, other.sri - 1, eci);
|
||||
if (der > 0) {
|
||||
addRet(other.eri + 1, sci, eri, eci);
|
||||
if (dsc > 0) {
|
||||
addRet(other.sri, sci, other.eri, other.sci - 1);
|
||||
}
|
||||
if (dec > 0) {
|
||||
addRet(other.sri, other.eci + 1, other.eri, eci);
|
||||
}
|
||||
} else {
|
||||
if (dsc > 0) {
|
||||
addRet(other.sri, sci, eri, other.sci - 1);
|
||||
}
|
||||
if (dec > 0) {
|
||||
addRet(other.sri, other.eci + 1, eri, eci);
|
||||
}
|
||||
}
|
||||
} else if (der > 0) {
|
||||
addRet(other.eri + 1, sci, eri, eci);
|
||||
if (dsc > 0) {
|
||||
addRet(sri, sci, other.eri, other.sci - 1);
|
||||
}
|
||||
if (dec > 0) {
|
||||
addRet(sri, other.eci + 1, other.eri, eci);
|
||||
}
|
||||
}
|
||||
if (dsc > 0) {
|
||||
addRet(sri, sci, eri, other.sci - 1);
|
||||
if (dec > 0) {
|
||||
addRet(sri, other.eri + 1, eri, eci);
|
||||
if (dsr > 0) {
|
||||
addRet(sri, other.sci, other.sri - 1, other.eci);
|
||||
}
|
||||
if (der > 0) {
|
||||
addRet(other.sri + 1, other.sci, eri, other.eci);
|
||||
}
|
||||
} else {
|
||||
if (dsr > 0) {
|
||||
addRet(sri, other.sci, other.sri - 1, eci);
|
||||
}
|
||||
if (der > 0) {
|
||||
addRet(other.sri + 1, other.sci, eri, eci);
|
||||
}
|
||||
}
|
||||
} else if (dec > 0) {
|
||||
addRet(eri, other.eci + 1, eri, eci);
|
||||
if (dsr > 0) {
|
||||
addRet(sri, sci, other.sri - 1, other.eci);
|
||||
}
|
||||
if (der > 0) {
|
||||
addRet(other.eri + 1, sci, eri, other.eci);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
size() {
|
||||
return [this.eri - this.sri + 1, this.eci - this.sci + 1];
|
||||
}
|
||||
|
||||
toString() {
|
||||
const { sri, sci, eri, eci } = this;
|
||||
let ref = xy2expr(sci, sri);
|
||||
if (this.multiple()) {
|
||||
ref = `${ref}:${xy2expr(eci, eri)}`;
|
||||
}
|
||||
return ref;
|
||||
}
|
||||
|
||||
clone() {
|
||||
const { sri, sci, eri, eci, w, h } = this;
|
||||
return new CellRange(sri, sci, eri, eci, w, h);
|
||||
}
|
||||
|
||||
/*
|
||||
toJSON() {
|
||||
return this.toString();
|
||||
}
|
||||
*/
|
||||
|
||||
equals(other) {
|
||||
return (
|
||||
this.eri === other.eri &&
|
||||
this.eci === other.eci &&
|
||||
this.sri === other.sri &&
|
||||
this.sci === other.sci
|
||||
);
|
||||
}
|
||||
|
||||
static valueOf(ref) {
|
||||
// B1:B8, B1 => 1 x 1 cell range
|
||||
const refs = ref.split(':');
|
||||
const [sci, sri] = expr2xy(refs[0]);
|
||||
let [eri, eci] = [sri, sci];
|
||||
if (refs.length > 1) {
|
||||
[eci, eri] = expr2xy(refs[1]);
|
||||
}
|
||||
return new CellRange(sri, sci, eri, eci);
|
||||
}
|
||||
}
|
||||
|
||||
export default CellRange;
|
||||
|
||||
export { CellRange };
|
||||
@@ -0,0 +1,35 @@
|
||||
export default class Clipboard {
|
||||
constructor() {
|
||||
this.range = null; // CellRange
|
||||
this.state = 'clear';
|
||||
}
|
||||
|
||||
copy(cellRange) {
|
||||
this.range = cellRange;
|
||||
this.state = 'copy';
|
||||
return this;
|
||||
}
|
||||
|
||||
cut(cellRange) {
|
||||
this.range = cellRange;
|
||||
this.state = 'cut';
|
||||
return this;
|
||||
}
|
||||
|
||||
isCopy() {
|
||||
return this.state === 'copy';
|
||||
}
|
||||
|
||||
isCut() {
|
||||
return this.state === 'cut';
|
||||
}
|
||||
|
||||
isClear() {
|
||||
return this.state === 'clear';
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.range = null;
|
||||
this.state = 'clear';
|
||||
}
|
||||
}
|
||||
80
OrangeFormsOpen-VUE3/src/components/SpreadSheet/core/col.js
Normal file
80
OrangeFormsOpen-VUE3/src/components/SpreadSheet/core/col.js
Normal file
@@ -0,0 +1,80 @@
|
||||
import helper from './helper.js';
|
||||
|
||||
class Cols {
|
||||
constructor({ len, width, indexWidth, minWidth }) {
|
||||
this._ = {};
|
||||
this.len = len;
|
||||
this.width = width;
|
||||
this.indexWidth = indexWidth;
|
||||
this.minWidth = minWidth;
|
||||
}
|
||||
|
||||
setData(d) {
|
||||
if (d.len) {
|
||||
this.len = d.len;
|
||||
delete d.len;
|
||||
}
|
||||
this._ = d;
|
||||
}
|
||||
|
||||
getData() {
|
||||
const { len } = this;
|
||||
return Object.assign({ len }, this._);
|
||||
}
|
||||
|
||||
getWidth(i) {
|
||||
if (this.isHide(i)) return 0;
|
||||
const col = this._[i];
|
||||
if (col && col.width) {
|
||||
return col.width;
|
||||
}
|
||||
return this.width;
|
||||
}
|
||||
|
||||
getOrNew(ci) {
|
||||
this._[ci] = this._[ci] || {};
|
||||
return this._[ci];
|
||||
}
|
||||
|
||||
setWidth(ci, width) {
|
||||
const col = this.getOrNew(ci);
|
||||
col.width = width;
|
||||
}
|
||||
|
||||
unhide(idx) {
|
||||
let index = idx;
|
||||
while (index > 0) {
|
||||
index -= 1;
|
||||
if (this.isHide(index)) {
|
||||
this.setHide(index, false);
|
||||
} else break;
|
||||
}
|
||||
}
|
||||
|
||||
isHide(ci) {
|
||||
const col = this._[ci];
|
||||
return col && col.hide;
|
||||
}
|
||||
|
||||
setHide(ci, v) {
|
||||
const col = this.getOrNew(ci);
|
||||
if (v === true) col.hide = true;
|
||||
else delete col.hide;
|
||||
}
|
||||
|
||||
setStyle(ci, style) {
|
||||
const col = this.getOrNew(ci);
|
||||
col.style = style;
|
||||
}
|
||||
|
||||
sumWidth(min, max) {
|
||||
return helper.rangeSum(min, max, i => this.getWidth(i));
|
||||
}
|
||||
|
||||
totalWidth() {
|
||||
return this.sumWidth(0, this.len);
|
||||
}
|
||||
}
|
||||
|
||||
export default {};
|
||||
export { Cols };
|
||||
1252
OrangeFormsOpen-VUE3/src/components/SpreadSheet/core/data_proxy.js
Normal file
1252
OrangeFormsOpen-VUE3/src/components/SpreadSheet/core/data_proxy.js
Normal file
File diff suppressed because it is too large
Load Diff
71
OrangeFormsOpen-VUE3/src/components/SpreadSheet/core/font.js
Normal file
71
OrangeFormsOpen-VUE3/src/components/SpreadSheet/core/font.js
Normal file
@@ -0,0 +1,71 @@
|
||||
// docs
|
||||
import './_.prototypes.js';
|
||||
|
||||
/** default font list
|
||||
* @type {BaseFont[]}
|
||||
*/
|
||||
const baseFonts = [
|
||||
{ key: 'Arial', title: 'Arial' },
|
||||
{ key: 'Helvetica', title: 'Helvetica' },
|
||||
{ key: 'Source Sans Pro', title: 'Source Sans Pro' },
|
||||
{ key: 'Comic Sans MS', title: 'Comic Sans MS' },
|
||||
{ key: 'Courier New', title: 'Courier New' },
|
||||
{ key: 'Verdana', title: 'Verdana' },
|
||||
{ key: 'Lato', title: 'Lato' },
|
||||
];
|
||||
|
||||
/** default fontSize list
|
||||
* @type {FontSize[]}
|
||||
*/
|
||||
const fontSizes = [
|
||||
{ pt: 7.5, px: 10 },
|
||||
{ pt: 8, px: 11 },
|
||||
{ pt: 9, px: 12 },
|
||||
{ pt: 10, px: 13 },
|
||||
{ pt: 10.5, px: 14 },
|
||||
{ pt: 11, px: 15 },
|
||||
{ pt: 12, px: 16 },
|
||||
{ pt: 14, px: 18.7 },
|
||||
{ pt: 15, px: 20 },
|
||||
{ pt: 16, px: 21.3 },
|
||||
{ pt: 18, px: 24 },
|
||||
{ pt: 22, px: 29.3 },
|
||||
{ pt: 24, px: 32 },
|
||||
{ pt: 26, px: 34.7 },
|
||||
{ pt: 36, px: 48 },
|
||||
{ pt: 42, px: 56 },
|
||||
// { pt: 54, px: 71.7 },
|
||||
// { pt: 63, px: 83.7 },
|
||||
// { pt: 72, px: 95.6 },
|
||||
];
|
||||
|
||||
/** map pt to px
|
||||
* @date 2019-10-10
|
||||
* @param {fontsizePT} pt
|
||||
* @returns {fontsizePX}
|
||||
*/
|
||||
function getFontSizePxByPt(pt) {
|
||||
for (let i = 0; i < fontSizes.length; i += 1) {
|
||||
const fontSize = fontSizes[i];
|
||||
if (fontSize.pt === pt) {
|
||||
return fontSize.px;
|
||||
}
|
||||
}
|
||||
return pt;
|
||||
}
|
||||
|
||||
/** transform baseFonts to map
|
||||
* @date 2019-10-10
|
||||
* @param {BaseFont[]} [ary=[]]
|
||||
* @returns {object}
|
||||
*/
|
||||
function fonts(ary = []) {
|
||||
const map = {};
|
||||
baseFonts.concat(ary).forEach(f => {
|
||||
map[f.key] = f;
|
||||
});
|
||||
return map;
|
||||
}
|
||||
|
||||
export default {};
|
||||
export { fontSizes, fonts, baseFonts, getFontSizePxByPt };
|
||||
106
OrangeFormsOpen-VUE3/src/components/SpreadSheet/core/format.js
Normal file
106
OrangeFormsOpen-VUE3/src/components/SpreadSheet/core/format.js
Normal file
@@ -0,0 +1,106 @@
|
||||
import { tf } from '../locale/locale.js';
|
||||
|
||||
const formatStringRender = v => v;
|
||||
|
||||
const formatNumberRender = v => {
|
||||
// match "-12.1" or "12" or "12.1"
|
||||
if (/^(-?\d*.?\d*)$/.test(v)) {
|
||||
const v1 = Number(v).toFixed(2).toString();
|
||||
const [first, ...parts] = v1.split('\\.');
|
||||
return [first.replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,'), ...parts];
|
||||
}
|
||||
return v;
|
||||
};
|
||||
|
||||
const baseFormats = [
|
||||
{
|
||||
key: 'normal',
|
||||
title: tf('format.normal'),
|
||||
type: 'string',
|
||||
render: formatStringRender,
|
||||
},
|
||||
{
|
||||
key: 'text',
|
||||
title: tf('format.text'),
|
||||
type: 'string',
|
||||
render: formatStringRender,
|
||||
},
|
||||
{
|
||||
key: 'number',
|
||||
title: tf('format.number'),
|
||||
type: 'number',
|
||||
label: '1,000.12',
|
||||
render: formatNumberRender,
|
||||
},
|
||||
{
|
||||
key: 'percent',
|
||||
title: tf('format.percent'),
|
||||
type: 'number',
|
||||
label: '10.12%',
|
||||
render: v => `${v}%`,
|
||||
},
|
||||
{
|
||||
key: 'rmb',
|
||||
title: tf('format.rmb'),
|
||||
type: 'number',
|
||||
label: '¥10.00',
|
||||
render: v => `¥${formatNumberRender(v)}`,
|
||||
},
|
||||
{
|
||||
key: 'usd',
|
||||
title: tf('format.usd'),
|
||||
type: 'number',
|
||||
label: '$10.00',
|
||||
render: v => `$${formatNumberRender(v)}`,
|
||||
},
|
||||
{
|
||||
key: 'eur',
|
||||
title: tf('format.eur'),
|
||||
type: 'number',
|
||||
label: '€10.00',
|
||||
render: v => `€${formatNumberRender(v)}`,
|
||||
},
|
||||
{
|
||||
key: 'date',
|
||||
title: tf('format.date'),
|
||||
type: 'date',
|
||||
label: '26/09/2008',
|
||||
render: formatStringRender,
|
||||
},
|
||||
{
|
||||
key: 'time',
|
||||
title: tf('format.time'),
|
||||
type: 'date',
|
||||
label: '15:59:00',
|
||||
render: formatStringRender,
|
||||
},
|
||||
{
|
||||
key: 'datetime',
|
||||
title: tf('format.datetime'),
|
||||
type: 'date',
|
||||
label: '26/09/2008 15:59:00',
|
||||
render: formatStringRender,
|
||||
},
|
||||
{
|
||||
key: 'duration',
|
||||
title: tf('format.duration'),
|
||||
type: 'date',
|
||||
label: '24:01:00',
|
||||
render: formatStringRender,
|
||||
},
|
||||
];
|
||||
|
||||
// const formats = (ary = []) => {
|
||||
// const map = {};
|
||||
// baseFormats.concat(ary).forEach((f) => {
|
||||
// map[f.key] = f;
|
||||
// });
|
||||
// return map;
|
||||
// };
|
||||
const formatm = {};
|
||||
baseFormats.forEach(f => {
|
||||
formatm[f.key] = f;
|
||||
});
|
||||
|
||||
export default {};
|
||||
export { formatm, baseFormats };
|
||||
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
formula:
|
||||
key
|
||||
title
|
||||
render
|
||||
*/
|
||||
/**
|
||||
* @typedef {object} Formula
|
||||
* @property {string} key
|
||||
* @property {function} title
|
||||
* @property {function} render
|
||||
*/
|
||||
import { tf } from '../locale/locale.js';
|
||||
import { numberCalc } from './helper.js';
|
||||
|
||||
/** @type {Formula[]} */
|
||||
const baseFormulas = [
|
||||
{
|
||||
key: 'SUM',
|
||||
title: tf('formula.sum'),
|
||||
render: ary => ary.reduce((a, b) => numberCalc('+', a, b), 0),
|
||||
},
|
||||
{
|
||||
key: 'AVERAGE',
|
||||
title: tf('formula.average'),
|
||||
render: ary => ary.reduce((a, b) => Number(a) + Number(b), 0) / ary.length,
|
||||
},
|
||||
{
|
||||
key: 'MAX',
|
||||
title: tf('formula.max'),
|
||||
render: ary => Math.max(...ary.map(v => Number(v))),
|
||||
},
|
||||
{
|
||||
key: 'MIN',
|
||||
title: tf('formula.min'),
|
||||
render: ary => Math.min(...ary.map(v => Number(v))),
|
||||
},
|
||||
{
|
||||
key: 'IF',
|
||||
title: tf('formula._if'),
|
||||
render: ([b, t, f]) => (b ? t : f),
|
||||
},
|
||||
{
|
||||
key: 'AND',
|
||||
title: tf('formula.and'),
|
||||
render: ary => ary.every(it => it),
|
||||
},
|
||||
{
|
||||
key: 'OR',
|
||||
title: tf('formula.or'),
|
||||
render: ary => ary.some(it => it),
|
||||
},
|
||||
{
|
||||
key: 'CONCAT',
|
||||
title: tf('formula.concat'),
|
||||
render: ary => ary.join(''),
|
||||
},
|
||||
/* support: 1 + A1 + B2 * 3
|
||||
{
|
||||
key: 'DIVIDE',
|
||||
title: tf('formula.divide'),
|
||||
render: ary => ary.reduce((a, b) => Number(a) / Number(b)),
|
||||
},
|
||||
{
|
||||
key: 'PRODUCT',
|
||||
title: tf('formula.product'),
|
||||
render: ary => ary.reduce((a, b) => Number(a) * Number(b),1),
|
||||
},
|
||||
{
|
||||
key: 'SUBTRACT',
|
||||
title: tf('formula.subtract'),
|
||||
render: ary => ary.reduce((a, b) => Number(a) - Number(b)),
|
||||
},
|
||||
*/
|
||||
];
|
||||
|
||||
const formulas = baseFormulas;
|
||||
|
||||
// const formulas = (formulaAry = []) => {
|
||||
// const formulaMap = {};
|
||||
// baseFormulas.concat(formulaAry).forEach((f) => {
|
||||
// formulaMap[f.key] = f;
|
||||
// });
|
||||
// return formulaMap;
|
||||
// };
|
||||
const formulam = {};
|
||||
baseFormulas.forEach(f => {
|
||||
formulam[f.key] = f;
|
||||
});
|
||||
|
||||
export default {};
|
||||
|
||||
export { formulam, formulas, baseFormulas };
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user