mirror of
https://github.com/apache/skywalking-booster-ui.git
synced 2025-10-14 03:09:18 +00:00
build: migrate the build tool from vue-cli to vite4 (#208)
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -19,11 +19,7 @@ limitations under the License. -->
|
||||
<div class="tools" @click="associateMetrics" v-if="associate.length">
|
||||
{{ t("associateMetrics") }}
|
||||
</div>
|
||||
<div
|
||||
class="tools"
|
||||
@click="viewTrace"
|
||||
v-if="relatedTrace && relatedTrace.enableRelate"
|
||||
>
|
||||
<div class="tools" @click="viewTrace" v-if="relatedTrace && relatedTrace.enableRelate">
|
||||
{{ t("viewTrace") }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -40,224 +36,212 @@ limitations under the License. -->
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
watch,
|
||||
ref,
|
||||
Ref,
|
||||
onMounted,
|
||||
onBeforeUnmount,
|
||||
unref,
|
||||
computed,
|
||||
} from "vue";
|
||||
import type { PropType } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { EventParams } from "@/types/app";
|
||||
import { Filters, RelatedTrace } from "@/types/dashboard";
|
||||
import { useECharts } from "@/hooks/useEcharts";
|
||||
import { addResizeListener, removeResizeListener } from "@/utils/event";
|
||||
import Trace from "@/views/dashboard/related/trace/Index.vue";
|
||||
import associateProcessor from "@/hooks/useAssociateProcessor";
|
||||
import { watch, ref, onMounted, onBeforeUnmount, unref, computed } from "vue";
|
||||
import type { PropType, Ref } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import type { EventParams } from "@/types/app";
|
||||
import type { Filters, RelatedTrace } from "@/types/dashboard";
|
||||
import { useECharts } from "@/hooks/useEcharts";
|
||||
import { addResizeListener, removeResizeListener } from "@/utils/event";
|
||||
import Trace from "@/views/dashboard/related/trace/Index.vue";
|
||||
import associateProcessor from "@/hooks/useAssociateProcessor";
|
||||
|
||||
/*global Nullable, defineProps, defineEmits*/
|
||||
const emits = defineEmits(["select"]);
|
||||
const { t } = useI18n();
|
||||
const chartRef = ref<Nullable<HTMLDivElement>>(null);
|
||||
const menus = ref<Nullable<HTMLDivElement>>(null);
|
||||
const visMenus = ref<boolean>(false);
|
||||
const { setOptions, resize, getInstance } = useECharts(
|
||||
chartRef as Ref<HTMLDivElement>
|
||||
);
|
||||
const currentParams = ref<Nullable<EventParams>>(null);
|
||||
const showTrace = ref<boolean>(false);
|
||||
const traceOptions = ref<{ type: string; filters?: unknown }>({
|
||||
type: "Trace",
|
||||
});
|
||||
const props = defineProps({
|
||||
height: { type: String, default: "100%" },
|
||||
width: { type: String, default: "100%" },
|
||||
option: {
|
||||
type: Object as PropType<{ [key: string]: any }>,
|
||||
default: () => ({}),
|
||||
},
|
||||
filters: {
|
||||
type: Object as PropType<Filters>,
|
||||
},
|
||||
relatedTrace: {
|
||||
type: Object as PropType<RelatedTrace>,
|
||||
},
|
||||
associate: {
|
||||
type: Array as PropType<{ widgetId: string }[]>,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
const available = computed(
|
||||
() =>
|
||||
(Array.isArray(props.option.series) &&
|
||||
props.option.series[0] &&
|
||||
props.option.series[0].data) ||
|
||||
(Array.isArray(props.option.series.data) && props.option.series.data[0])
|
||||
);
|
||||
onMounted(async () => {
|
||||
await setOptions(props.option);
|
||||
chartRef.value && addResizeListener(unref(chartRef), resize);
|
||||
instanceEvent();
|
||||
});
|
||||
/*global Nullable, defineProps, defineEmits*/
|
||||
const emits = defineEmits(["select"]);
|
||||
const { t } = useI18n();
|
||||
const chartRef = ref<Nullable<HTMLDivElement>>(null);
|
||||
const menus = ref<Nullable<HTMLDivElement>>(null);
|
||||
const visMenus = ref<boolean>(false);
|
||||
const { setOptions, resize, getInstance } = useECharts(chartRef as Ref<HTMLDivElement>);
|
||||
const currentParams = ref<Nullable<EventParams>>(null);
|
||||
const showTrace = ref<boolean>(false);
|
||||
const traceOptions = ref<{ type: string; filters?: unknown }>({
|
||||
type: "Trace",
|
||||
});
|
||||
const props = defineProps({
|
||||
height: { type: String, default: "100%" },
|
||||
width: { type: String, default: "100%" },
|
||||
option: {
|
||||
type: Object as PropType<{ [key: string]: any }>,
|
||||
default: () => ({}),
|
||||
},
|
||||
filters: {
|
||||
type: Object as PropType<Filters>,
|
||||
},
|
||||
relatedTrace: {
|
||||
type: Object as PropType<RelatedTrace>,
|
||||
},
|
||||
associate: {
|
||||
type: Array as PropType<{ widgetId: string }[]>,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
const available = computed(
|
||||
() =>
|
||||
(Array.isArray(props.option.series) && props.option.series[0] && props.option.series[0].data) ||
|
||||
(Array.isArray(props.option.series.data) && props.option.series.data[0]),
|
||||
);
|
||||
onMounted(async () => {
|
||||
await setOptions(props.option);
|
||||
chartRef.value && addResizeListener(unref(chartRef), resize);
|
||||
instanceEvent();
|
||||
});
|
||||
|
||||
function instanceEvent() {
|
||||
setTimeout(() => {
|
||||
function instanceEvent() {
|
||||
setTimeout(() => {
|
||||
const instance = getInstance();
|
||||
|
||||
if (!instance) {
|
||||
return;
|
||||
}
|
||||
instance.on("click", (params: EventParams) => {
|
||||
currentParams.value = params;
|
||||
if (!menus.value || !chartRef.value) {
|
||||
return;
|
||||
}
|
||||
visMenus.value = true;
|
||||
const w = chartRef.value.getBoundingClientRect().width || 0;
|
||||
const h = chartRef.value.getBoundingClientRect().height || 0;
|
||||
if (w - params.event.offsetX > 120) {
|
||||
menus.value.style.left = params.event.offsetX + "px";
|
||||
} else {
|
||||
menus.value.style.left = params.event.offsetX - 120 + "px";
|
||||
}
|
||||
if (h - params.event.offsetY < 50) {
|
||||
menus.value.style.top = params.event.offsetY - 40 + "px";
|
||||
} else {
|
||||
menus.value.style.top = params.event.offsetY + 2 + "px";
|
||||
}
|
||||
});
|
||||
document.addEventListener(
|
||||
"click",
|
||||
() => {
|
||||
if (instance.isDisposed()) {
|
||||
return;
|
||||
}
|
||||
visMenus.value = false;
|
||||
instance.dispatchAction({
|
||||
type: "updateAxisPointer",
|
||||
currTrigger: "leave",
|
||||
});
|
||||
},
|
||||
true,
|
||||
);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
function associateMetrics() {
|
||||
emits("select", currentParams.value);
|
||||
const { dataIndex, seriesIndex } = currentParams.value || {
|
||||
dataIndex: 0,
|
||||
seriesIndex: 0,
|
||||
};
|
||||
updateOptions({ dataIndex, seriesIndex });
|
||||
}
|
||||
|
||||
function updateOptions(params?: { dataIndex: number; seriesIndex: number }) {
|
||||
const instance = getInstance();
|
||||
|
||||
if (!instance) {
|
||||
return;
|
||||
}
|
||||
instance.on("click", (params: EventParams) => {
|
||||
currentParams.value = params;
|
||||
if (!menus.value || !chartRef.value) {
|
||||
if (!props.filters) {
|
||||
return;
|
||||
}
|
||||
if (props.filters.isRange) {
|
||||
const { eventAssociate } = associateProcessor(props);
|
||||
const options = eventAssociate();
|
||||
setOptions(options || props.option);
|
||||
} else {
|
||||
instance.dispatchAction({
|
||||
type: "updateAxisPointer",
|
||||
dataIndex: params ? params.dataIndex : props.filters.dataIndex,
|
||||
seriesIndex: params ? params.seriesIndex : 0,
|
||||
});
|
||||
const ids = props.option.series.map((_: unknown, index: number) => index);
|
||||
instance.dispatchAction({
|
||||
type: "highlight",
|
||||
dataIndex: params ? params.dataIndex : props.filters.dataIndex,
|
||||
seriesIndex: ids,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function viewTrace() {
|
||||
const item = associateProcessor(props).traceFilters(currentParams.value);
|
||||
traceOptions.value = {
|
||||
...traceOptions.value,
|
||||
filters: item,
|
||||
};
|
||||
showTrace.value = true;
|
||||
visMenus.value = true;
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.option,
|
||||
(newVal, oldVal) => {
|
||||
if (!available.value) {
|
||||
return;
|
||||
}
|
||||
visMenus.value = true;
|
||||
const w = chartRef.value.getBoundingClientRect().width || 0;
|
||||
const h = chartRef.value.getBoundingClientRect().height || 0;
|
||||
if (w - params.event.offsetX > 120) {
|
||||
menus.value.style.left = params.event.offsetX + "px";
|
||||
} else {
|
||||
menus.value.style.left = params.event.offsetX - 120 + "px";
|
||||
if (JSON.stringify(newVal) === JSON.stringify(oldVal)) {
|
||||
return;
|
||||
}
|
||||
if (h - params.event.offsetY < 50) {
|
||||
menus.value.style.top = params.event.offsetY - 40 + "px";
|
||||
} else {
|
||||
menus.value.style.top = params.event.offsetY + 2 + "px";
|
||||
let options;
|
||||
if (props.filters && props.filters.isRange) {
|
||||
const { eventAssociate } = associateProcessor(props);
|
||||
options = eventAssociate();
|
||||
}
|
||||
});
|
||||
document.addEventListener(
|
||||
"click",
|
||||
() => {
|
||||
if (instance.isDisposed()) {
|
||||
return;
|
||||
}
|
||||
visMenus.value = false;
|
||||
instance.dispatchAction({
|
||||
type: "updateAxisPointer",
|
||||
currTrigger: "leave",
|
||||
});
|
||||
},
|
||||
true
|
||||
);
|
||||
}, 1000);
|
||||
}
|
||||
setOptions(options || props.option);
|
||||
},
|
||||
);
|
||||
watch(
|
||||
() => props.filters,
|
||||
() => {
|
||||
updateOptions();
|
||||
},
|
||||
);
|
||||
|
||||
function associateMetrics() {
|
||||
emits("select", currentParams.value);
|
||||
const { dataIndex, seriesIndex } = currentParams.value || {
|
||||
dataIndex: 0,
|
||||
seriesIndex: 0,
|
||||
};
|
||||
updateOptions({ dataIndex, seriesIndex });
|
||||
}
|
||||
|
||||
function updateOptions(params?: { dataIndex: number; seriesIndex: number }) {
|
||||
const instance = getInstance();
|
||||
if (!instance) {
|
||||
return;
|
||||
}
|
||||
if (!props.filters) {
|
||||
return;
|
||||
}
|
||||
if (props.filters.isRange) {
|
||||
const { eventAssociate } = associateProcessor(props);
|
||||
const options = eventAssociate();
|
||||
setOptions(options || props.option);
|
||||
} else {
|
||||
instance.dispatchAction({
|
||||
type: "updateAxisPointer",
|
||||
dataIndex: params ? params.dataIndex : props.filters.dataIndex,
|
||||
seriesIndex: params ? params.seriesIndex : 0,
|
||||
});
|
||||
const ids = props.option.series.map((_: unknown, index: number) => index);
|
||||
instance.dispatchAction({
|
||||
type: "highlight",
|
||||
dataIndex: params ? params.dataIndex : props.filters.dataIndex,
|
||||
seriesIndex: ids,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function viewTrace() {
|
||||
const item = associateProcessor(props).traceFilters(currentParams.value);
|
||||
traceOptions.value = {
|
||||
...traceOptions.value,
|
||||
filters: item,
|
||||
};
|
||||
showTrace.value = true;
|
||||
visMenus.value = true;
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.option,
|
||||
(newVal, oldVal) => {
|
||||
if (!available.value) {
|
||||
return;
|
||||
}
|
||||
if (JSON.stringify(newVal) === JSON.stringify(oldVal)) {
|
||||
return;
|
||||
}
|
||||
let options;
|
||||
if (props.filters && props.filters.isRange) {
|
||||
const { eventAssociate } = associateProcessor(props);
|
||||
options = eventAssociate();
|
||||
}
|
||||
setOptions(options || props.option);
|
||||
}
|
||||
);
|
||||
watch(
|
||||
() => props.filters,
|
||||
() => {
|
||||
updateOptions();
|
||||
}
|
||||
);
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
removeResizeListener(unref(chartRef), resize);
|
||||
});
|
||||
onBeforeUnmount(() => {
|
||||
removeResizeListener(unref(chartRef), resize);
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.no-data {
|
||||
font-size: 12px;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: horizontal;
|
||||
-webkit-box-pack: center;
|
||||
-webkit-box-align: center;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.chart {
|
||||
overflow: hidden;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.menus {
|
||||
position: absolute;
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
z-index: 9999999;
|
||||
box-shadow: #ddd 1px 2px 10px;
|
||||
transition: all cubic-bezier(0.075, 0.82, 0.165, 1) linear;
|
||||
background-color: rgb(255, 255, 255);
|
||||
border-radius: 4px;
|
||||
color: rgb(51, 51, 51);
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.tools {
|
||||
padding: 5px;
|
||||
color: #999;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: #409eff;
|
||||
background-color: #eee;
|
||||
.no-data {
|
||||
font-size: 12px;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: horizontal;
|
||||
-webkit-box-pack: center;
|
||||
-webkit-box-align: center;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.chart {
|
||||
overflow: hidden;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.menus {
|
||||
position: absolute;
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
z-index: 9999999;
|
||||
box-shadow: #ddd 1px 2px 10px;
|
||||
transition: all cubic-bezier(0.075, 0.82, 0.165, 1) linear;
|
||||
background-color: rgb(255, 255, 255);
|
||||
border-radius: 4px;
|
||||
color: rgb(51, 51, 51);
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.tools {
|
||||
padding: 5px;
|
||||
color: #999;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: #409eff;
|
||||
background-color: #eee;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -24,64 +24,62 @@ limitations under the License. -->
|
||||
loading,
|
||||
}"
|
||||
>
|
||||
<use :xlink:href="`#${iconName}`"></use>
|
||||
<use :href="`#${iconName}`"></use>
|
||||
</svg>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import "@/assets/icons/index";
|
||||
|
||||
/*global defineProps */
|
||||
defineProps({
|
||||
iconName: { type: String, default: "" },
|
||||
size: { type: String, default: "sm" },
|
||||
loading: { type: Boolean, default: false },
|
||||
});
|
||||
/*global defineProps */
|
||||
defineProps({
|
||||
iconName: { type: String, default: "" },
|
||||
size: { type: String, default: "sm" },
|
||||
loading: { type: Boolean, default: false },
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
vertical-align: middle;
|
||||
fill: currentColor;
|
||||
.icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
vertical-align: middle;
|
||||
fill: currentColor;
|
||||
|
||||
&.sm {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
&.sm {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
&.middle {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
&.middle {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
&.lg {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
&.lg {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
|
||||
&.loading {
|
||||
animation: loading 1.5s linear infinite;
|
||||
}
|
||||
&.loading {
|
||||
animation: loading 1.5s linear infinite;
|
||||
}
|
||||
|
||||
&.logo {
|
||||
height: 30px;
|
||||
width: 110px;
|
||||
}
|
||||
&.logo {
|
||||
height: 30px;
|
||||
width: 110px;
|
||||
}
|
||||
|
||||
&.xl {
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
}
|
||||
}
|
||||
@keyframes loading {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
&.xl {
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
}
|
||||
}
|
||||
@keyframes loading {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
to {
|
||||
-webkit-transform: rotate(1turn);
|
||||
transform: rotate(1turn);
|
||||
to {
|
||||
-webkit-transform: rotate(1turn);
|
||||
transform: rotate(1turn);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -20,31 +20,31 @@ limitations under the License. -->
|
||||
</el-radio-group>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
import type { PropType } from "vue";
|
||||
import { ref } from "vue";
|
||||
import type { PropType } from "vue";
|
||||
|
||||
interface Option {
|
||||
label: string | number;
|
||||
value: string | number;
|
||||
}
|
||||
/*global defineProps, defineEmits */
|
||||
const emit = defineEmits(["change"]);
|
||||
const props = defineProps({
|
||||
options: {
|
||||
type: Array as PropType<
|
||||
{
|
||||
label: string | number;
|
||||
value: string | number;
|
||||
}[]
|
||||
>,
|
||||
default: () => [],
|
||||
},
|
||||
value: {
|
||||
type: String as PropType<string>,
|
||||
default: "",
|
||||
},
|
||||
size: { type: null, default: "default" },
|
||||
});
|
||||
|
||||
/*global defineProps, defineEmits */
|
||||
const emit = defineEmits(["change"]);
|
||||
const props = defineProps({
|
||||
options: {
|
||||
type: Array as PropType<Option[]>,
|
||||
default: () => [],
|
||||
},
|
||||
value: {
|
||||
type: String as PropType<string>,
|
||||
default: "",
|
||||
},
|
||||
size: { type: null, default: "default" },
|
||||
});
|
||||
const selected = ref<string>(props.value);
|
||||
|
||||
const selected = ref<string>(props.value);
|
||||
|
||||
function checked(opt: unknown) {
|
||||
emit("change", opt);
|
||||
}
|
||||
function checked(opt: unknown) {
|
||||
emit("change", opt);
|
||||
}
|
||||
</script>
|
||||
|
@@ -19,9 +19,7 @@ limitations under the License. -->
|
||||
{{ selected.label }}
|
||||
</span>
|
||||
<span class="no-data" v-else>Please select a option</span>
|
||||
<span class="remove-icon" @click="removeSelected" v-if="clearable">
|
||||
×
|
||||
</span>
|
||||
<span class="remove-icon" @click="removeSelected" v-if="clearable"> × </span>
|
||||
</div>
|
||||
<div class="opt-wrapper" v-show="visible">
|
||||
<div
|
||||
@@ -37,141 +35,141 @@ limitations under the License. -->
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch } from "vue";
|
||||
import type { PropType } from "vue";
|
||||
import { Option } from "@/types/app";
|
||||
import { ref, watch } from "vue";
|
||||
import type { PropType } from "vue";
|
||||
import type { Option } from "@/types/app";
|
||||
|
||||
/*global defineProps, defineEmits*/
|
||||
const emit = defineEmits(["change"]);
|
||||
const props = defineProps({
|
||||
options: {
|
||||
type: Array as PropType<Option[]>,
|
||||
default: () => [],
|
||||
},
|
||||
value: {
|
||||
type: String as PropType<string>,
|
||||
default: () => "",
|
||||
},
|
||||
clearable: { type: Boolean, default: false },
|
||||
});
|
||||
const visible = ref<boolean>(false);
|
||||
const opt = props.options.find((d: Option) => props.value === d.value);
|
||||
const selected = ref<Option>(opt || { label: "", value: "" });
|
||||
/*global defineProps, defineEmits*/
|
||||
const emit = defineEmits(["change"]);
|
||||
const props = defineProps({
|
||||
options: {
|
||||
type: Array as PropType<Option[]>,
|
||||
default: () => [],
|
||||
},
|
||||
value: {
|
||||
type: String as PropType<string>,
|
||||
default: () => "",
|
||||
},
|
||||
clearable: { type: Boolean, default: false },
|
||||
});
|
||||
const visible = ref<boolean>(false);
|
||||
const opt = props.options.find((d: Option) => props.value === d.value);
|
||||
const selected = ref<Option>(opt || { label: "", value: "" });
|
||||
|
||||
function handleSelect(i: Option) {
|
||||
selected.value = i;
|
||||
emit("change", i.value);
|
||||
}
|
||||
function removeSelected() {
|
||||
selected.value = { label: "", value: "" };
|
||||
emit("change", "");
|
||||
}
|
||||
watch(
|
||||
() => props.value,
|
||||
(data) => {
|
||||
const opt = props.options.find((d: Option) => data === d.value);
|
||||
selected.value = opt || { label: "", value: "" };
|
||||
function handleSelect(i: Option) {
|
||||
selected.value = i;
|
||||
emit("change", i.value);
|
||||
}
|
||||
);
|
||||
document.body.addEventListener("click", handleClick, false);
|
||||
function removeSelected() {
|
||||
selected.value = { label: "", value: "" };
|
||||
emit("change", "");
|
||||
}
|
||||
watch(
|
||||
() => props.value,
|
||||
(data) => {
|
||||
const opt = props.options.find((d: Option) => data === d.value);
|
||||
selected.value = opt || { label: "", value: "" };
|
||||
},
|
||||
);
|
||||
document.body.addEventListener("click", handleClick, false);
|
||||
|
||||
function handleClick() {
|
||||
visible.value = false;
|
||||
}
|
||||
function setPopper(event: any) {
|
||||
event.stopPropagation();
|
||||
visible.value = !visible.value;
|
||||
}
|
||||
function handleClick() {
|
||||
visible.value = false;
|
||||
}
|
||||
function setPopper(event: any) {
|
||||
event.stopPropagation();
|
||||
visible.value = !visible.value;
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.bar-select {
|
||||
position: relative;
|
||||
justify-content: space-between;
|
||||
border: 1px solid #ddd;
|
||||
background: #fff;
|
||||
border-radius: 3px;
|
||||
color: #000;
|
||||
font-size: 12px;
|
||||
height: 24px;
|
||||
|
||||
.selected {
|
||||
padding: 0 3px;
|
||||
.bar-select {
|
||||
position: relative;
|
||||
justify-content: space-between;
|
||||
border: 1px solid #ddd;
|
||||
background: #fff;
|
||||
border-radius: 3px;
|
||||
margin: 3px;
|
||||
color: #409eff;
|
||||
background-color: #fafafa;
|
||||
border: 1px solid #e8e8e8;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
color: #000;
|
||||
font-size: 12px;
|
||||
height: 24px;
|
||||
|
||||
.no-data {
|
||||
color: #c0c4cc;
|
||||
}
|
||||
|
||||
.bar-i {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 2px 10px;
|
||||
overflow: auto;
|
||||
color: #606266;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
.remove-icon {
|
||||
display: block;
|
||||
.selected {
|
||||
padding: 0 3px;
|
||||
border-radius: 3px;
|
||||
margin: 3px;
|
||||
color: #409eff;
|
||||
background-color: #fafafa;
|
||||
border: 1px solid #e8e8e8;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.remove-icon {
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 0;
|
||||
font-size: 14px;
|
||||
display: none;
|
||||
color: #aaa;
|
||||
cursor: pointer;
|
||||
}
|
||||
.no-data {
|
||||
color: #c0c4cc;
|
||||
}
|
||||
|
||||
.opt-wrapper {
|
||||
color: #606266;
|
||||
position: absolute;
|
||||
top: 26px;
|
||||
left: 0;
|
||||
background: #fff;
|
||||
box-shadow: 0 1px 6px rgba(99, 99, 99, 0.2);
|
||||
border: 1px solid #ddd;
|
||||
width: 100%;
|
||||
border-radius: 0 0 3px 3px;
|
||||
border-right-width: 1px !important;
|
||||
z-index: 10;
|
||||
overflow: auto;
|
||||
max-height: 200px;
|
||||
padding-bottom: 2px;
|
||||
|
||||
.close {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 12px;
|
||||
opacity: 0.6;
|
||||
.bar-i {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 2px 10px;
|
||||
overflow: auto;
|
||||
color: #606266;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
.remove-icon {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.opt {
|
||||
padding: 7px 15px;
|
||||
|
||||
&.select-disabled {
|
||||
color: #409eff;
|
||||
cursor: not-allowed;
|
||||
.remove-icon {
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 0;
|
||||
font-size: 14px;
|
||||
display: none;
|
||||
color: #aaa;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #f5f5f5;
|
||||
.opt-wrapper {
|
||||
color: #606266;
|
||||
position: absolute;
|
||||
top: 26px;
|
||||
left: 0;
|
||||
background: #fff;
|
||||
box-shadow: 0 1px 6px rgba(99, 99, 99, 0.2);
|
||||
border: 1px solid #ddd;
|
||||
width: 100%;
|
||||
border-radius: 0 0 3px 3px;
|
||||
border-right-width: 1px !important;
|
||||
z-index: 10;
|
||||
overflow: auto;
|
||||
max-height: 200px;
|
||||
padding-bottom: 2px;
|
||||
|
||||
.close {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 12px;
|
||||
opacity: 0.6;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.opt {
|
||||
padding: 7px 15px;
|
||||
|
||||
&.select-disabled {
|
||||
color: #409eff;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -27,73 +27,71 @@ limitations under the License. -->
|
||||
:remote-method="remoteMethod"
|
||||
:filterable="filterable"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in options"
|
||||
:key="item.value || ''"
|
||||
:label="item.label || ''"
|
||||
:value="item.value || ''"
|
||||
>
|
||||
<el-option v-for="item in options" :key="item.value || ''" :label="item.label || ''" :value="item.value || ''">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch } from "vue";
|
||||
import type { PropType } from "vue";
|
||||
import { ref, watch } from "vue";
|
||||
import type { PropType } from "vue";
|
||||
|
||||
interface Option {
|
||||
label: string | number;
|
||||
value: string | number;
|
||||
}
|
||||
// interface Option {
|
||||
// label: string | number;
|
||||
// value: string | number;
|
||||
// }
|
||||
|
||||
/*global defineProps, defineEmits*/
|
||||
const emit = defineEmits(["change", "query"]);
|
||||
const props = defineProps({
|
||||
options: {
|
||||
type: Array as PropType<(Option & { disabled?: boolean })[]>,
|
||||
default: () => [],
|
||||
},
|
||||
value: {
|
||||
type: [Array, String, Number, undefined] as PropType<any>,
|
||||
default: () => [],
|
||||
},
|
||||
size: { type: null, default: "default" },
|
||||
placeholder: {
|
||||
type: [String, undefined] as PropType<string>,
|
||||
default: "Select a option",
|
||||
},
|
||||
borderRadius: { type: Number, default: 3 },
|
||||
multiple: { type: Boolean, default: false },
|
||||
disabled: { type: Boolean, default: false },
|
||||
clearable: { type: Boolean, default: false },
|
||||
isRemote: { type: Boolean, default: false },
|
||||
filterable: { type: Boolean, default: true },
|
||||
});
|
||||
/*global defineProps, defineEmits*/
|
||||
const emit = defineEmits(["change", "query"]);
|
||||
const props = defineProps({
|
||||
options: {
|
||||
type: Array as PropType<
|
||||
({
|
||||
label: string | number;
|
||||
value: string | number;
|
||||
} & { disabled?: boolean })[]
|
||||
>,
|
||||
default: () => [],
|
||||
},
|
||||
value: {
|
||||
type: [Array, String, Number, undefined] as PropType<any>,
|
||||
default: () => [],
|
||||
},
|
||||
size: { type: null, default: "default" },
|
||||
placeholder: {
|
||||
type: [String, undefined] as PropType<string>,
|
||||
default: "Select a option",
|
||||
},
|
||||
borderRadius: { type: Number, default: 3 },
|
||||
multiple: { type: Boolean, default: false },
|
||||
disabled: { type: Boolean, default: false },
|
||||
clearable: { type: Boolean, default: false },
|
||||
isRemote: { type: Boolean, default: false },
|
||||
filterable: { type: Boolean, default: true },
|
||||
});
|
||||
|
||||
const selected = ref<string[] | string>(props.value);
|
||||
function changeSelected() {
|
||||
const options = props.options.filter((d: any) =>
|
||||
props.multiple
|
||||
? selected.value.includes(d.value)
|
||||
: selected.value === d.value
|
||||
const selected = ref<string[] | string>(props.value);
|
||||
function changeSelected() {
|
||||
const options = props.options.filter((d: any) =>
|
||||
props.multiple ? selected.value.includes(d.value) : selected.value === d.value,
|
||||
);
|
||||
emit("change", options);
|
||||
}
|
||||
|
||||
function remoteMethod(query: string) {
|
||||
if (props.isRemote) {
|
||||
emit("query", query);
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.value,
|
||||
(data) => {
|
||||
selected.value = data;
|
||||
},
|
||||
);
|
||||
emit("change", options);
|
||||
}
|
||||
|
||||
function remoteMethod(query: string) {
|
||||
if (props.isRemote) {
|
||||
emit("query", query);
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.value,
|
||||
(data) => {
|
||||
selected.value = data;
|
||||
}
|
||||
);
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.el-input__inner {
|
||||
border-radius: unset !important;
|
||||
}
|
||||
.el-input__inner {
|
||||
border-radius: unset !important;
|
||||
}
|
||||
</style>
|
||||
|
@@ -35,56 +35,28 @@ limitations under the License. -->
|
||||
<transition name="datepicker-anim">
|
||||
<div
|
||||
class="datepicker-popup"
|
||||
:class="[
|
||||
popupClass,
|
||||
{ 'datepicker-inline': type === 'inline' },
|
||||
position,
|
||||
]"
|
||||
:class="[popupClass, { 'datepicker-inline': type === 'inline' }, position]"
|
||||
tabindex="-1"
|
||||
v-if="show || type === 'inline'"
|
||||
>
|
||||
<template v-if="range">
|
||||
<div class="datepicker-popup__sidebar">
|
||||
<button
|
||||
type="button"
|
||||
class="datepicker-popup__shortcut"
|
||||
@click="quickPick('quarter')"
|
||||
>
|
||||
<button type="button" class="datepicker-popup__shortcut" @click="quickPick('quarter')">
|
||||
{{ local.quarterHourCutTip }}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="datepicker-popup__shortcut"
|
||||
@click="quickPick('half')"
|
||||
>
|
||||
<button type="button" class="datepicker-popup__shortcut" @click="quickPick('half')">
|
||||
{{ local.halfHourCutTip }}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="datepicker-popup__shortcut"
|
||||
@click="quickPick('hour')"
|
||||
>
|
||||
<button type="button" class="datepicker-popup__shortcut" @click="quickPick('hour')">
|
||||
{{ local.hourCutTip }}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="datepicker-popup__shortcut"
|
||||
@click="quickPick('day')"
|
||||
>
|
||||
<button type="button" class="datepicker-popup__shortcut" @click="quickPick('day')">
|
||||
{{ local.dayCutTip }}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="datepicker-popup__shortcut"
|
||||
@click="quickPick('week')"
|
||||
>
|
||||
<button type="button" class="datepicker-popup__shortcut" @click="quickPick('week')">
|
||||
{{ local.weekCutTip }}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="datepicker-popup__shortcut"
|
||||
@click="quickPick('month')"
|
||||
>
|
||||
<button type="button" class="datepicker-popup__shortcut" @click="quickPick('month')">
|
||||
{{ local.monthCutTip }}
|
||||
</button>
|
||||
</div>
|
||||
@@ -123,16 +95,10 @@ limitations under the License. -->
|
||||
/>
|
||||
</template>
|
||||
<div v-if="showButtons" class="datepicker__buttons">
|
||||
<button
|
||||
@click.prevent.stop="cancel"
|
||||
class="datepicker__button-cancel"
|
||||
>
|
||||
<button @click.prevent.stop="cancel" class="datepicker__button-cancel">
|
||||
{{ local.cancelTip }}
|
||||
</button>
|
||||
<button
|
||||
@click.prevent.stop="submit"
|
||||
class="datepicker__button-select"
|
||||
>
|
||||
<button @click.prevent.stop="submit" class="datepicker__button-select">
|
||||
{{ local.submitTip }}
|
||||
</button>
|
||||
</div>
|
||||
@@ -142,438 +108,431 @@ limitations under the License. -->
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted, onBeforeUnmount, watch } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import DateCalendar from "./DateCalendar.vue";
|
||||
import { useTimeoutFn } from "@/hooks/useTimeout";
|
||||
/*global defineProps, defineEmits */
|
||||
const datepicker = ref(null);
|
||||
const { t } = useI18n();
|
||||
const show = ref<boolean>(false);
|
||||
const dates = ref<Date | string[] | any>([]);
|
||||
const props = defineProps({
|
||||
position: { type: String, default: "bottom" },
|
||||
name: [String],
|
||||
inputClass: [String],
|
||||
popupClass: [String],
|
||||
value: [Date, Array, String],
|
||||
disabled: [Boolean],
|
||||
type: {
|
||||
type: String,
|
||||
default: "normal",
|
||||
},
|
||||
rangeSeparator: {
|
||||
type: String,
|
||||
default: "~",
|
||||
},
|
||||
clearable: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
placeholder: [String],
|
||||
disabledDate: {
|
||||
type: Function,
|
||||
default: () => false,
|
||||
},
|
||||
format: {
|
||||
type: String,
|
||||
default: "YYYY-MM-DD",
|
||||
},
|
||||
showButtons: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
dateRangeSelect: [Function],
|
||||
});
|
||||
const emit = defineEmits(["clear", "input", "confirm", "cancel"]);
|
||||
const local = computed(() => {
|
||||
return {
|
||||
dow: 1, // Monday is the first day of the week
|
||||
hourTip: t("hourTip"), // tip of select hour
|
||||
minuteTip: t("minuteTip"), // tip of select minute
|
||||
secondTip: t("secondTip"), // tip of select second
|
||||
yearSuffix: t("yearSuffix"), // format of head
|
||||
monthsHead: t("monthsHead").split("_"), // months of head
|
||||
months: t("months").split("_"), // months of panel
|
||||
weeks: t("weeks").split("_"), // weeks
|
||||
cancelTip: t("cancel"), // default text for cancel button
|
||||
submitTip: t("confirm"), // default text for submit button
|
||||
quarterHourCutTip: t("quarterHourCutTip"),
|
||||
halfHourCutTip: t("halfHourCutTip"),
|
||||
hourCutTip: t("hourCutTip"),
|
||||
dayCutTip: t("dayCutTip"),
|
||||
weekCutTip: t("weekCutTip"),
|
||||
monthCutTip: t("monthCutTip"),
|
||||
import { ref, computed, onMounted, onBeforeUnmount, watch } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import DateCalendar from "./DateCalendar.vue";
|
||||
import { useTimeoutFn } from "@/hooks/useTimeout";
|
||||
/*global defineProps, defineEmits */
|
||||
const datepicker = ref(null);
|
||||
const { t } = useI18n();
|
||||
const show = ref<boolean>(false);
|
||||
const dates = ref<Date | string[] | any>([]);
|
||||
const props = defineProps({
|
||||
position: { type: String, default: "bottom" },
|
||||
name: [String],
|
||||
inputClass: [String],
|
||||
popupClass: [String],
|
||||
value: [Date, Array, String],
|
||||
disabled: [Boolean],
|
||||
type: {
|
||||
type: String,
|
||||
default: "normal",
|
||||
},
|
||||
rangeSeparator: {
|
||||
type: String,
|
||||
default: "~",
|
||||
},
|
||||
clearable: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
placeholder: [String],
|
||||
disabledDate: {
|
||||
type: Function,
|
||||
default: () => false,
|
||||
},
|
||||
format: {
|
||||
type: String,
|
||||
default: "YYYY-MM-DD",
|
||||
},
|
||||
showButtons: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
dateRangeSelect: [Function],
|
||||
});
|
||||
const emit = defineEmits(["clear", "input", "confirm", "cancel"]);
|
||||
const local = computed(() => {
|
||||
return {
|
||||
dow: 1, // Monday is the first day of the week
|
||||
hourTip: t("hourTip"), // tip of select hour
|
||||
minuteTip: t("minuteTip"), // tip of select minute
|
||||
secondTip: t("secondTip"), // tip of select second
|
||||
yearSuffix: t("yearSuffix"), // format of head
|
||||
monthsHead: t("monthsHead").split("_"), // months of head
|
||||
months: t("months").split("_"), // months of panel
|
||||
weeks: t("weeks").split("_"), // weeks
|
||||
cancelTip: t("cancel"), // default text for cancel button
|
||||
submitTip: t("confirm"), // default text for submit button
|
||||
quarterHourCutTip: t("quarterHourCutTip"),
|
||||
halfHourCutTip: t("halfHourCutTip"),
|
||||
hourCutTip: t("hourCutTip"),
|
||||
dayCutTip: t("dayCutTip"),
|
||||
weekCutTip: t("weekCutTip"),
|
||||
monthCutTip: t("monthCutTip"),
|
||||
};
|
||||
});
|
||||
const tf = (time: Date, format?: any): string => {
|
||||
const year = time.getFullYear();
|
||||
const month = time.getMonth();
|
||||
const day = time.getDate();
|
||||
const hours24 = time.getHours();
|
||||
const hours = hours24 % 12 === 0 ? 12 : hours24 % 12;
|
||||
const minutes = time.getMinutes();
|
||||
const seconds = time.getSeconds();
|
||||
const milliseconds = time.getMilliseconds();
|
||||
const dd = (t: number) => `0${t}`.slice(-2);
|
||||
const map: { [key: string]: string | number } = {
|
||||
YYYY: year,
|
||||
MM: dd(month + 1),
|
||||
MMM: local.value.months[month],
|
||||
MMMM: local.value.monthsHead[month],
|
||||
M: month + 1,
|
||||
DD: dd(day),
|
||||
D: day,
|
||||
HH: dd(hours24),
|
||||
H: hours24,
|
||||
hh: dd(hours),
|
||||
h: hours,
|
||||
mm: dd(minutes),
|
||||
m: minutes,
|
||||
ss: dd(seconds),
|
||||
s: seconds,
|
||||
S: milliseconds,
|
||||
};
|
||||
return (format || props.format).replace(/Y+|M+|D+|H+|h+|m+|s+|S+/g, (str: string) => map[str]);
|
||||
};
|
||||
});
|
||||
const tf = (time: Date, format?: any): string => {
|
||||
const year = time.getFullYear();
|
||||
const month = time.getMonth();
|
||||
const day = time.getDate();
|
||||
const hours24 = time.getHours();
|
||||
const hours = hours24 % 12 === 0 ? 12 : hours24 % 12;
|
||||
const minutes = time.getMinutes();
|
||||
const seconds = time.getSeconds();
|
||||
const milliseconds = time.getMilliseconds();
|
||||
const dd = (t: number) => `0${t}`.slice(-2);
|
||||
const map: { [key: string]: string | number } = {
|
||||
YYYY: year,
|
||||
MM: dd(month + 1),
|
||||
MMM: local.value.months[month],
|
||||
MMMM: local.value.monthsHead[month],
|
||||
M: month + 1,
|
||||
DD: dd(day),
|
||||
D: day,
|
||||
HH: dd(hours24),
|
||||
H: hours24,
|
||||
hh: dd(hours),
|
||||
h: hours,
|
||||
mm: dd(minutes),
|
||||
m: minutes,
|
||||
ss: dd(seconds),
|
||||
s: seconds,
|
||||
S: milliseconds,
|
||||
const range = computed(() => {
|
||||
return dates.value.length === 2;
|
||||
});
|
||||
const text = computed(() => {
|
||||
const val = props.value;
|
||||
const txt = dates.value.map((date: Date) => tf(date)).join(` ${props.rangeSeparator} `);
|
||||
if (Array.isArray(val)) {
|
||||
return val.length > 1 ? txt : "";
|
||||
}
|
||||
return val ? txt : "";
|
||||
});
|
||||
const get = () => {
|
||||
return Array.isArray(props.value) ? dates.value : dates.value[0];
|
||||
};
|
||||
return (format || props.format).replace(
|
||||
/Y+|M+|D+|H+|h+|m+|s+|S+/g,
|
||||
(str: string) => map[str]
|
||||
const cls = () => {
|
||||
emit("clear");
|
||||
emit("input", range.value ? [] : "");
|
||||
};
|
||||
const vi = (val: any) => {
|
||||
if (Array.isArray(val)) {
|
||||
return val.length > 1 ? val.map((item) => new Date(item)) : [new Date(), new Date()];
|
||||
}
|
||||
return val ? [new Date(val)] : [new Date()];
|
||||
};
|
||||
const ok = (leaveOpened: boolean) => {
|
||||
emit("input", get());
|
||||
!leaveOpened &&
|
||||
!props.showButtons &&
|
||||
useTimeoutFn(() => {
|
||||
show.value = range.value;
|
||||
}, 1);
|
||||
};
|
||||
const setDates = (d: Date, pos: string) => {
|
||||
if (pos === "right") {
|
||||
dates.value[1] = d;
|
||||
return;
|
||||
}
|
||||
dates.value[0] = d;
|
||||
};
|
||||
const dc = (e: any) => {
|
||||
show.value = (datepicker.value as any).contains(e.target) && !props.disabled;
|
||||
};
|
||||
const quickPick = (type: string) => {
|
||||
const end = new Date();
|
||||
const start = new Date();
|
||||
switch (type) {
|
||||
case "quarter":
|
||||
start.setTime(start.getTime() - 60 * 15 * 1000); //15 mins
|
||||
break;
|
||||
case "half":
|
||||
start.setTime(start.getTime() - 60 * 30 * 1000); //30 mins
|
||||
break;
|
||||
case "hour":
|
||||
start.setTime(start.getTime() - 3600 * 1000); //1 hour
|
||||
break;
|
||||
case "day":
|
||||
start.setTime(start.getTime() - 3600 * 1000 * 24); //1 day
|
||||
break;
|
||||
case "week":
|
||||
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7); //1 week
|
||||
break;
|
||||
case "month":
|
||||
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30); //1 month
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
dates.value = [start, end];
|
||||
emit("input", get());
|
||||
};
|
||||
const submit = () => {
|
||||
emit("confirm", get());
|
||||
show.value = false;
|
||||
};
|
||||
const cancel = () => {
|
||||
emit("cancel");
|
||||
show.value = false;
|
||||
};
|
||||
onMounted(() => {
|
||||
dates.value = vi(props.value);
|
||||
document.addEventListener("click", dc, true);
|
||||
});
|
||||
onBeforeUnmount(() => {
|
||||
document.removeEventListener("click", dc, true);
|
||||
});
|
||||
watch(
|
||||
() => props.value,
|
||||
(val: unknown) => {
|
||||
dates.value = vi(val);
|
||||
},
|
||||
);
|
||||
};
|
||||
const range = computed(() => {
|
||||
return dates.value.length === 2;
|
||||
});
|
||||
const text = computed(() => {
|
||||
const val = props.value;
|
||||
const txt = dates.value
|
||||
.map((date: Date) => tf(date))
|
||||
.join(` ${props.rangeSeparator} `);
|
||||
if (Array.isArray(val)) {
|
||||
return val.length > 1 ? txt : "";
|
||||
}
|
||||
return val ? txt : "";
|
||||
});
|
||||
const get = () => {
|
||||
return Array.isArray(props.value) ? dates.value : dates.value[0];
|
||||
};
|
||||
const cls = () => {
|
||||
emit("clear");
|
||||
emit("input", range.value ? [] : "");
|
||||
};
|
||||
const vi = (val: any) => {
|
||||
if (Array.isArray(val)) {
|
||||
return val.length > 1
|
||||
? val.map((item) => new Date(item))
|
||||
: [new Date(), new Date()];
|
||||
}
|
||||
return val ? [new Date(val)] : [new Date()];
|
||||
};
|
||||
const ok = (leaveOpened: boolean) => {
|
||||
emit("input", get());
|
||||
!leaveOpened &&
|
||||
!props.showButtons &&
|
||||
useTimeoutFn(() => {
|
||||
show.value = range.value;
|
||||
}, 1);
|
||||
};
|
||||
const setDates = (d: Date, pos: string) => {
|
||||
if (pos === "right") {
|
||||
dates.value[1] = d;
|
||||
return;
|
||||
}
|
||||
dates.value[0] = d;
|
||||
};
|
||||
const dc = (e: any) => {
|
||||
show.value = (datepicker.value as any).contains(e.target) && !props.disabled;
|
||||
};
|
||||
const quickPick = (type: string) => {
|
||||
const end = new Date();
|
||||
const start = new Date();
|
||||
switch (type) {
|
||||
case "quarter":
|
||||
start.setTime(start.getTime() - 60 * 15 * 1000); //15 mins
|
||||
break;
|
||||
case "half":
|
||||
start.setTime(start.getTime() - 60 * 30 * 1000); //30 mins
|
||||
break;
|
||||
case "hour":
|
||||
start.setTime(start.getTime() - 3600 * 1000); //1 hour
|
||||
break;
|
||||
case "day":
|
||||
start.setTime(start.getTime() - 3600 * 1000 * 24); //1 day
|
||||
break;
|
||||
case "week":
|
||||
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7); //1 week
|
||||
break;
|
||||
case "month":
|
||||
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30); //1 month
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
dates.value = [start, end];
|
||||
emit("input", get());
|
||||
};
|
||||
const submit = () => {
|
||||
emit("confirm", get());
|
||||
show.value = false;
|
||||
};
|
||||
const cancel = () => {
|
||||
emit("cancel");
|
||||
show.value = false;
|
||||
};
|
||||
onMounted(() => {
|
||||
dates.value = vi(props.value);
|
||||
document.addEventListener("click", dc, true);
|
||||
});
|
||||
onBeforeUnmount(() => {
|
||||
document.removeEventListener("click", dc, true);
|
||||
});
|
||||
watch(
|
||||
() => props.value,
|
||||
(val: unknown) => {
|
||||
dates.value = vi(val);
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@keyframes datepicker-anim-in {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: scaleY(0.8);
|
||||
}
|
||||
@keyframes datepicker-anim-in {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: scaleY(0.8);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scaleY(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes datepicker-anim-out {
|
||||
0% {
|
||||
opacity: 1;
|
||||
transform: scaleY(1);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: scaleY(0.8);
|
||||
}
|
||||
}
|
||||
|
||||
.datepicker {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.datepicker-icon {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
left: 8px;
|
||||
color: #515a6ecc;
|
||||
}
|
||||
|
||||
.datepicker-close {
|
||||
display: none;
|
||||
position: absolute;
|
||||
width: 34px;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
right: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.datepicker-close:before {
|
||||
display: block;
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
margin-left: -8px;
|
||||
margin-top: -8px;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
border-radius: 50%;
|
||||
background: #ccc
|
||||
url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA3IDciIHdpZHRoPSI3IiBoZWlnaHQ9IjciPjxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik01LjU4LDVsMi44LTIuODFBLjQxLjQxLDAsMSwwLDcuOCwxLjZMNSw0LjQxLDIuMiwxLjZhLjQxLjQxLDAsMCwwLS41OC41OGgwTDQuNDIsNSwxLjYyLDcuOGEuNDEuNDEsMCwwLDAsLjU4LjU4TDUsNS41OCw3LjgsOC4zOWEuNDEuNDEsMCwwLDAsLjU4LS41OGgwWiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTEuNSAtMS40OCkiIHN0eWxlPSJmaWxsOiNmZmYiLz48L3N2Zz4NCg==")
|
||||
no-repeat 50% 50%;
|
||||
}
|
||||
|
||||
.datepicker__clearable:hover:before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.datepicker__clearable:hover .datepicker-close {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.datepicker-close:hover:before {
|
||||
background-color: #afafaf;
|
||||
}
|
||||
|
||||
.datepicker > input {
|
||||
color: inherit;
|
||||
// transition: all 200ms ease;
|
||||
border-radius: 4px;
|
||||
border: 0;
|
||||
background: none;
|
||||
height: 28px;
|
||||
box-sizing: border-box;
|
||||
outline: none;
|
||||
padding: 0 5px;
|
||||
width: 100%;
|
||||
user-select: none;
|
||||
font-family: "Monaco";
|
||||
letter-spacing: -0.7px;
|
||||
}
|
||||
|
||||
// .datepicker > input.focus {
|
||||
// border-color: #3f97e3;
|
||||
// -webkit-box-shadow: 0 0 5px rgba(59, 180, 242, 0.3);
|
||||
// box-shadow: 0 0 5px rgba(59, 180, 242, 0.3);
|
||||
// }
|
||||
|
||||
.datepicker > input:disabled {
|
||||
cursor: not-allowed;
|
||||
background-color: #ebebe4;
|
||||
border-color: #e5e5e5;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.datepicker-popup {
|
||||
border-radius: 4px;
|
||||
position: absolute;
|
||||
transition: all 200ms ease;
|
||||
opacity: 1;
|
||||
transform: scaleY(1);
|
||||
font-size: 12px;
|
||||
background: #fff;
|
||||
box-shadow: 0 1px 6px rgba(99, 99, 99, 0.2);
|
||||
margin-top: 2px;
|
||||
outline: 0;
|
||||
padding: 5px;
|
||||
overflow: hidden;
|
||||
z-index: 999;
|
||||
|
||||
&.top {
|
||||
bottom: 30px;
|
||||
right: 0;
|
||||
transform-origin: center bottom;
|
||||
}
|
||||
|
||||
&.bottom {
|
||||
top: 30px;
|
||||
right: 0;
|
||||
transform-origin: center top;
|
||||
}
|
||||
|
||||
&.left {
|
||||
top: 30px;
|
||||
transform-origin: center top;
|
||||
}
|
||||
|
||||
&.right {
|
||||
right: -80px;
|
||||
top: 30px;
|
||||
transform-origin: center top;
|
||||
}
|
||||
|
||||
&__sidebar {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 100px;
|
||||
height: 100%;
|
||||
padding: 5px;
|
||||
border-right: solid 1px #eaeaea;
|
||||
}
|
||||
|
||||
&__shortcut {
|
||||
display: block;
|
||||
width: 100%;
|
||||
border: 0;
|
||||
background-color: transparent;
|
||||
line-height: 34px;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
text-align: left;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
|
||||
&:hover {
|
||||
color: #3f97e3;
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scaleY(1);
|
||||
}
|
||||
}
|
||||
|
||||
&__body {
|
||||
margin-left: 100px;
|
||||
padding-left: 5px;
|
||||
@keyframes datepicker-anim-out {
|
||||
0% {
|
||||
opacity: 1;
|
||||
transform: scaleY(1);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: scaleY(0.8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.datepicker-inline {
|
||||
position: relative;
|
||||
margin-top: 0;
|
||||
}
|
||||
.datepicker {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.datepicker-range {
|
||||
min-width: 238px;
|
||||
}
|
||||
.datepicker-icon {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
left: 8px;
|
||||
color: #515a6ecc;
|
||||
}
|
||||
|
||||
.datepicker-range .datepicker-popup {
|
||||
width: 520px;
|
||||
}
|
||||
.datepicker-close {
|
||||
display: none;
|
||||
position: absolute;
|
||||
width: 34px;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
right: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.datepicker-bottom {
|
||||
float: left;
|
||||
width: 100%;
|
||||
text-align: right;
|
||||
}
|
||||
.datepicker-close:before {
|
||||
display: block;
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
margin-left: -8px;
|
||||
margin-top: -8px;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
border-radius: 50%;
|
||||
background: #ccc
|
||||
url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA3IDciIHdpZHRoPSI3IiBoZWlnaHQ9IjciPjxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik01LjU4LDVsMi44LTIuODFBLjQxLjQxLDAsMSwwLDcuOCwxLjZMNSw0LjQxLDIuMiwxLjZhLjQxLjQxLDAsMCwwLS41OC41OGgwTDQuNDIsNSwxLjYyLDcuOGEuNDEuNDEsMCwwLDAsLjU4LjU4TDUsNS41OCw3LjgsOC4zOWEuNDEuNDEsMCwwLDAsLjU4LS41OGgwWiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTEuNSAtMS40OCkiIHN0eWxlPSJmaWxsOiNmZmYiLz48L3N2Zz4NCg==")
|
||||
no-repeat 50% 50%;
|
||||
}
|
||||
|
||||
.datepicker-btn {
|
||||
padding: 5px 10px;
|
||||
background: #3f97e3;
|
||||
color: #fff;
|
||||
border-radius: 2px;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
}
|
||||
.datepicker__clearable:hover:before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.datepicker-anim-enter-active {
|
||||
transform-origin: 0 0;
|
||||
animation: datepicker-anim-in 0.2s cubic-bezier(0.23, 1, 0.32, 1);
|
||||
}
|
||||
.datepicker__clearable:hover .datepicker-close {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.datepicker-anim-leave-active {
|
||||
transform-origin: 0 0;
|
||||
animation: datepicker-anim-out 0.2s cubic-bezier(0.755, 0.05, 0.855, 0.06);
|
||||
}
|
||||
.datepicker-close:hover:before {
|
||||
background-color: #afafaf;
|
||||
}
|
||||
|
||||
.datepicker__buttons {
|
||||
display: block;
|
||||
text-align: right;
|
||||
}
|
||||
.datepicker > input {
|
||||
color: inherit;
|
||||
// transition: all 200ms ease;
|
||||
border-radius: 4px;
|
||||
border: 0;
|
||||
background: none;
|
||||
height: 28px;
|
||||
box-sizing: border-box;
|
||||
outline: none;
|
||||
padding: 0 5px;
|
||||
width: 100%;
|
||||
user-select: none;
|
||||
font-family: "Monaco";
|
||||
letter-spacing: -0.7px;
|
||||
}
|
||||
|
||||
.datepicker__buttons button {
|
||||
display: inline-block;
|
||||
font-size: 13px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
margin: 10px 0 0 5px;
|
||||
padding: 5px 15px;
|
||||
color: #ffffff;
|
||||
}
|
||||
// .datepicker > input.focus {
|
||||
// border-color: #3f97e3;
|
||||
// -webkit-box-shadow: 0 0 5px rgba(59, 180, 242, 0.3);
|
||||
// box-shadow: 0 0 5px rgba(59, 180, 242, 0.3);
|
||||
// }
|
||||
|
||||
.datepicker__buttons .datepicker__button-select {
|
||||
background: #3f97e3;
|
||||
}
|
||||
.datepicker > input:disabled {
|
||||
cursor: not-allowed;
|
||||
background-color: #ebebe4;
|
||||
border-color: #e5e5e5;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.datepicker__buttons .datepicker__button-cancel {
|
||||
background: #666;
|
||||
}
|
||||
.datepicker-popup {
|
||||
border-radius: 4px;
|
||||
position: absolute;
|
||||
transition: all 200ms ease;
|
||||
opacity: 1;
|
||||
transform: scaleY(1);
|
||||
font-size: 12px;
|
||||
background: #fff;
|
||||
box-shadow: 0 1px 6px rgba(99, 99, 99, 0.2);
|
||||
margin-top: 2px;
|
||||
outline: 0;
|
||||
padding: 5px;
|
||||
overflow: hidden;
|
||||
z-index: 999;
|
||||
|
||||
&.top {
|
||||
bottom: 30px;
|
||||
right: 0;
|
||||
transform-origin: center bottom;
|
||||
}
|
||||
|
||||
&.bottom {
|
||||
top: 30px;
|
||||
right: 0;
|
||||
transform-origin: center top;
|
||||
}
|
||||
|
||||
&.left {
|
||||
top: 30px;
|
||||
transform-origin: center top;
|
||||
}
|
||||
|
||||
&.right {
|
||||
right: -80px;
|
||||
top: 30px;
|
||||
transform-origin: center top;
|
||||
}
|
||||
|
||||
&__sidebar {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 100px;
|
||||
height: 100%;
|
||||
padding: 5px;
|
||||
border-right: solid 1px #eaeaea;
|
||||
}
|
||||
|
||||
&__shortcut {
|
||||
display: block;
|
||||
width: 100%;
|
||||
border: 0;
|
||||
background-color: transparent;
|
||||
line-height: 34px;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
text-align: left;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
|
||||
&:hover {
|
||||
color: #3f97e3;
|
||||
}
|
||||
}
|
||||
|
||||
&__body {
|
||||
margin-left: 100px;
|
||||
padding-left: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.datepicker-inline {
|
||||
position: relative;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.datepicker-range {
|
||||
min-width: 238px;
|
||||
}
|
||||
|
||||
.datepicker-range .datepicker-popup {
|
||||
width: 520px;
|
||||
}
|
||||
|
||||
.datepicker-bottom {
|
||||
float: left;
|
||||
width: 100%;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.datepicker-btn {
|
||||
padding: 5px 10px;
|
||||
background: #3f97e3;
|
||||
color: #fff;
|
||||
border-radius: 2px;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.datepicker-anim-enter-active {
|
||||
transform-origin: 0 0;
|
||||
animation: datepicker-anim-in 0.2s cubic-bezier(0.23, 1, 0.32, 1);
|
||||
}
|
||||
|
||||
.datepicker-anim-leave-active {
|
||||
transform-origin: 0 0;
|
||||
animation: datepicker-anim-out 0.2s cubic-bezier(0.755, 0.05, 0.855, 0.06);
|
||||
}
|
||||
|
||||
.datepicker__buttons {
|
||||
display: block;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.datepicker__buttons button {
|
||||
display: inline-block;
|
||||
font-size: 13px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
margin: 10px 0 0 5px;
|
||||
padding: 5px 15px;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.datepicker__buttons .datepicker__button-select {
|
||||
background: #3f97e3;
|
||||
}
|
||||
|
||||
.datepicker__buttons .datepicker__button-cancel {
|
||||
background: #666;
|
||||
}
|
||||
</style>
|
||||
|
33
src/components/__tests__/HelloWorld.spec.ts
Normal file
33
src/components/__tests__/HelloWorld.spec.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { describe, it } from "vitest";
|
||||
|
||||
// import { mount } from '@vue/test-utils'
|
||||
// import HelloWorld from '../HelloWorld.vue'
|
||||
|
||||
// describe('HelloWorld', () => {
|
||||
// it('renders properly', () => {
|
||||
// const wrapper = mount(HelloWorld, { props: { msg: 'Hello Vitest' } })
|
||||
// expect(wrapper.text()).toContain('Hello Vitest')
|
||||
// })
|
||||
// })
|
||||
describe("My First Test", () => {
|
||||
it("renders props.msg when passed", () => {
|
||||
const msg = "new message";
|
||||
console.log(msg);
|
||||
});
|
||||
});
|
Reference in New Issue
Block a user