| | |
| | | @font-face { |
| | | font-family: "ywicon"; /* Project id 4499025 */ |
| | | src: url('iconfont.woff2?t=1721375418093') format('woff2'), |
| | | url('iconfont.woff?t=1721375418093') format('woff'), |
| | | url('iconfont.ttf?t=1721375418093') format('truetype'); |
| | | src: url('iconfont.woff2?t=1721966908249') format('woff2'), |
| | | url('iconfont.woff?t=1721966908249') format('woff'), |
| | | url('iconfont.ttf?t=1721966908249') format('truetype'); |
| | | } |
| | | |
| | | .ywicon { |
| | |
| | | -moz-osx-font-smoothing: grayscale; |
| | | } |
| | | |
| | | .icon-pre:before { |
| | | content: "\e61b"; |
| | | } |
| | | |
| | | .icon-next:before { |
| | | content: "\e61c"; |
| | | } |
| | | |
| | | .icon-maikefeng-filled:before { |
| | | content: "\e621"; |
| | | } |
| | |
| | | originData: { |
| | | type: Object as PropType<any>, |
| | | }, |
| | | |
| | | } as const); |
| | | export type ChatComPropsType = ExtractPropTypes<typeof chatComProps>; |
| | | |
| | |
| | | grid: { |
| | | // bottom: 120, |
| | | // right: '15%', |
| | | top: 65, |
| | | left: 35, |
| | | right: 45, |
| | | }, |
| | | tooltip: { |
| | | show: true, |
| | | trigger: 'axis', |
| | | |
| | | }, |
| | | toolbox: { |
| | | show: true, |
| | |
| | | @change="(val) => handleQueryChange(val, item)" |
| | | :disabled="chartLoading" |
| | | ></component> |
| | | <YRange v-model="yRange" @input="yRangeInput" /> |
| | | <el-checkbox class="m-1" v-model="isMultiCompare" label="夿¥å¯¹æ¯" @change="multiCompareChange"></el-checkbox> |
| | | </div> |
| | | <div class="h-[20rem]" v-resize="chartContainerResize" v-loading="chartLoading"> |
| | | <div ref="chartRef"></div> |
| | |
| | | <script setup lang="ts"> |
| | | import type * as echarts from 'echarts'; |
| | | import _ from 'lodash'; |
| | | import moment from 'moment'; |
| | | import type { PropType } from 'vue'; |
| | | import { computed, ref, watch } from 'vue'; |
| | | import { SCATTER_SYMBOL_SIZE, chatComProps, getChatChartOption } from '../../../common'; |
| | | import { computed, ref } from 'vue'; |
| | | import { SCATTER_SYMBOL_SIZE, getChatChartOption } from '../../../common'; |
| | | import { useDrawChatChart } from '../../../hooks/useDrawChatChart'; |
| | | import YRange from './components/YRange.vue'; |
| | | import type { RecordSet, RecordSetParamsItem } from './types'; |
| | | import { recordSetMapCom } from './types'; |
| | | import { filterQuery } from '/@/api/ai/chat'; |
| | | import { deepClone } from '/@/utils/other'; |
| | | import { debounce } from '/@/utils/util'; |
| | | |
| | | const chartRef = ref<HTMLDivElement>(null); |
| | | const defaultDisplayType = 'line'; |
| | | |
| | | const yRange = ref({ |
| | | min: null as number, |
| | | max: null as number, |
| | | }); |
| | | // const props = defineProps({ |
| | | // data: { |
| | | // type: Object as PropType<RecordSet>, |
| | |
| | | let groupedValues = null; |
| | | let timeIndex = undefined; |
| | | let valueIndex = undefined; |
| | | let nameIndex = undefined; |
| | | |
| | | let timeCol = null; |
| | | let valueCol = null; |
| | | |
| | | let preData = null; |
| | | |
| | | const setNewOption = (series?: any[], extraOption: echarts.EChartsOption = {}) => { |
| | | const isEmpty = !series || series.length === 0; |
| | | if (isEmpty) { |
| | | series = Object.keys(groupedValues).map((item) => { |
| | | const values = groupedValues[item]; |
| | | return { |
| | | name: item === 'default' ? '' : item, |
| | | data: values.map((item) => [item[timeIndex], item[valueIndex]]), |
| | | type: defaultDisplayType, |
| | | symbol: 'none', |
| | | smooth: true, |
| | | }; |
| | | }); |
| | | } |
| | | const combineOption = _.defaultsDeep(extraOption, getChatChartOption(), { |
| | | grid: { |
| | | bottom: 20, |
| | | }, |
| | | legend: { |
| | | top: 19, |
| | | show: series?.length > 1, |
| | | type: 'scroll', |
| | | }, |
| | | toolbox: { |
| | | show: true, |
| | | feature: { |
| | | myBar: { |
| | | onclick: () => { |
| | | chartInstance.value.setOption({ |
| | | series: series.map((item) => ({ |
| | | ...item, |
| | | type: 'bar', |
| | | symbol: 'none', |
| | | })), |
| | | }); |
| | | }, |
| | | }, |
| | | |
| | | myScatter: { |
| | | onclick: () => { |
| | | chartInstance.value.setOption({ |
| | | series: series.map((item) => ({ |
| | | ...item, |
| | | type: 'scatter', |
| | | symbol: 'circle', |
| | | symbolSize: SCATTER_SYMBOL_SIZE, |
| | | })), |
| | | }); |
| | | }, |
| | | }, |
| | | myLine: { |
| | | onclick: () => { |
| | | chartInstance.value.setOption({ |
| | | series: series.map((item) => ({ |
| | | ...item, |
| | | type: 'line', |
| | | symbol: 'none', |
| | | smooth: true, |
| | | })), |
| | | }); |
| | | }, |
| | | }, |
| | | }, |
| | | }, |
| | | |
| | | title: { |
| | | text: preData?.title, |
| | | }, |
| | | xAxis: { |
| | | name: timeCol?.title, |
| | | }, |
| | | yAxis: { |
| | | name: valueCol?.title, |
| | | /** @description ä¸å¼ºå¶ä¿ç0 */ |
| | | scale: true, |
| | | }, |
| | | series: series, |
| | | } as echarts.EChartsOption); |
| | | chartInstance.value.setOption(combineOption, { |
| | | notMerge: true, |
| | | }); |
| | | }; |
| | | |
| | | const drawChart = () => { |
| | | const data = props.data; |
| | | preData = data; |
| | | const xType = 'time'; |
| | | timeIndex = data.cols.findIndex((item) => item.type === 'time'); |
| | | if (timeIndex === -1) { |
| | | timeIndex = 0; |
| | | } |
| | | const timeCol = data.cols[timeIndex]; |
| | | timeCol = data.cols[timeIndex]; |
| | | |
| | | valueIndex = data.cols.findIndex((item) => item.type === 'value'); |
| | | if (valueIndex === -1) { |
| | | valueIndex = 2; |
| | | } |
| | | const valueCol = data.cols[valueIndex]; |
| | | valueCol = data.cols[valueIndex]; |
| | | |
| | | let nameCol = null; |
| | | groupedValues = null; |
| | | if (data.chart === 'muli_line') { |
| | | let nameIndex = data.cols.findIndex((item) => item.type === 'name'); |
| | | nameIndex = data.cols.findIndex((item) => item.type === 'name'); |
| | | if (nameIndex === -1) { |
| | | nameIndex = 1; |
| | | } |
| | |
| | | groupedValues = _.groupBy(data.values, (item) => item[nameIndex]); |
| | | } |
| | | |
| | | const seriesData = Object.keys(groupedValues).map((item) => { |
| | | const values = groupedValues[item]; |
| | | return { |
| | | name: item === 'default' ? '' : item, |
| | | data: values.map((item) => [item[timeIndex], item[valueIndex]]), |
| | | type: defaultDisplayType, |
| | | symbol: 'none', |
| | | smooth: true, |
| | | }; |
| | | }); |
| | | const combineOption = _.defaultsDeep(getChatChartOption(), { |
| | | grid: { |
| | | bottom: 20, |
| | | }, |
| | | toolbox: { |
| | | show: true, |
| | | feature: { |
| | | myBar: { |
| | | onclick: () => { |
| | | chartInstance.value.setOption({ |
| | | series: seriesData.map((item) => ({ |
| | | ...item, |
| | | type: 'bar', |
| | | symbol: 'none', |
| | | })), |
| | | }); |
| | | }, |
| | | }, |
| | | |
| | | myScatter: { |
| | | onclick: () => { |
| | | chartInstance.value.setOption({ |
| | | series: seriesData.map((item) => ({ |
| | | ...item, |
| | | type: 'scatter', |
| | | symbol: 'circle', |
| | | symbolSize: SCATTER_SYMBOL_SIZE, |
| | | })), |
| | | }); |
| | | }, |
| | | }, |
| | | myLine: { |
| | | onclick: () => { |
| | | chartInstance.value.setOption({ |
| | | series: seriesData.map((item) => ({ |
| | | ...item, |
| | | type: 'line', |
| | | symbol: 'none', |
| | | smooth: true, |
| | | })), |
| | | }); |
| | | }, |
| | | }, |
| | | }, |
| | | }, |
| | | |
| | | title: { |
| | | text: data?.title, |
| | | }, |
| | | xAxis: { |
| | | name: timeCol?.title, |
| | | }, |
| | | yAxis: { |
| | | name: valueCol?.title, |
| | | }, |
| | | series: seriesData, |
| | | } as echarts.EChartsOption); |
| | | chartInstance.value.setOption(combineOption); |
| | | setNewOption(); |
| | | }; |
| | | const { chartContainerResize, chartInstance } = useDrawChatChart({ chartRef, drawChart }); |
| | | |
| | |
| | | |
| | | const title = res?.values?.title; |
| | | const values = res?.values?.values ?? []; |
| | | groupedValues = _.groupBy(values, (item) => item[nameIndex]); |
| | | |
| | | if (isMultiCompare.value) { |
| | | handleMultiCompare(); |
| | | } else { |
| | | chartInstance.value.setOption({ |
| | | title: { |
| | | text: title, |
| | | }, |
| | | series: |
| | | groupedValues && |
| | | Object.keys(groupedValues).map((item) => { |
| | | const values = groupedValues[item]; |
| | | return { |
| | | data: values.map((item) => [item[timeIndex], item[valueIndex]]), |
| | | }; |
| | | }), |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | //#region ====================== 设置Yèå´ ====================== |
| | | const debounceSetYRange = debounce((val) => { |
| | | chartInstance.value.setOption({ |
| | | title: { |
| | | text: title, |
| | | yAxis: { |
| | | min: val.min, |
| | | max: val.max, |
| | | }, |
| | | series: |
| | | groupedValues && |
| | | Object.keys(groupedValues).map(() => { |
| | | return { |
| | | data: values.map((item) => [item[timeIndex], item[valueIndex]]), |
| | | }; |
| | | }), |
| | | }); |
| | | }); |
| | | |
| | | const yRangeInput = (val) => { |
| | | debounceSetYRange(val); |
| | | }; |
| | | |
| | | //#endregion |
| | | |
| | | //#region ====================== 夿¥å¯¹æ¯ ====================== |
| | | // 夿¥å¯¹æ¯åºåæ¶é´ |
| | | const COMMON_DAY = '2024-07-26'; |
| | | |
| | | const isMultiCompare = ref(false); |
| | | const handleMultiCompare = () => { |
| | | if (!isMultiCompare.value) return; |
| | | const cloneData = deepClone(groupedValues); |
| | | const seriesData = Object.keys(cloneData).reduce((preVal, curVal, curIndex, arr) => { |
| | | const values = cloneData[curVal]; |
| | | const isMulti = arr.length > 1; |
| | | const groupByDateValues = _.groupBy(values, (item) => moment(item[timeIndex]).format('YYYY-MM-DD')); |
| | | for (const key in groupByDateValues) { |
| | | if (Object.prototype.hasOwnProperty.call(groupByDateValues, key)) { |
| | | const val = groupByDateValues[key]; |
| | | |
| | | const newVal = val.map((item) => { |
| | | // æ°åç§° |
| | | item[nameIndex] = isMulti ? `${curVal}_${key}` : `${key}`; |
| | | item[timeIndex] = COMMON_DAY + ' ' + moment(item[timeIndex]).format('HH:mm:ss'); |
| | | return item; |
| | | }); |
| | | |
| | | preVal.push(newVal); |
| | | } |
| | | } |
| | | return preVal; |
| | | }, []); |
| | | const series = seriesData.map<echarts.SeriesOption>((item) => ({ |
| | | name: item[0]?.[nameIndex], |
| | | data: item.map((item) => [item[timeIndex], item[valueIndex]]), |
| | | type: defaultDisplayType, |
| | | symbol: 'none', |
| | | smooth: true, |
| | | })); |
| | | setNewOption(series, { |
| | | tooltip: { |
| | | show: true, |
| | | trigger: 'axis', |
| | | formatter(params) { |
| | | const itemList = params.map((item, index) => { |
| | | return `<div style="margin: ${index === 0 ? 0 : 10}px 0 0; line-height: 1"> |
| | | <div style="margin: 0px 0 0; line-height: 1"> |
| | | ${item.marker}<span style="font-size: 14px; color: #666; font-weight: 400; margin-left: 2px" |
| | | >${item.seriesName}</span |
| | | ><span style="float: right; margin-left: 20px; font-size: 14px; color: #666; font-weight: 900">${item.data[1]}</span> |
| | | <div style="clear: both"></div> |
| | | </div> |
| | | <div style="clear: both"></div> |
| | | </div>`; |
| | | }); |
| | | |
| | | const result = `<div style="margin: 0px 0 0; line-height: 1"> |
| | | <div style="margin: 0px 0 0; line-height: 1"> |
| | | <div style="font-size: 14px; color: #666; font-weight: 400; line-height: 1">${params?.[0]?.data[0]?.slice(10, 16)}</div> |
| | | <div style="margin: 10px 0 0; line-height: 1"> |
| | | ${itemList.join('')} |
| | | <div style="clear: both"></div> |
| | | </div> |
| | | <div style="clear: both"></div> |
| | | </div> |
| | | <div style="clear: both"></div> |
| | | </div>`; |
| | | return result; |
| | | }, |
| | | }, |
| | | xAxis: { |
| | | min: COMMON_DAY + ' 00:00:00', |
| | | max: COMMON_DAY + ' 23:59:59', |
| | | splitNumber: 10, |
| | | axisLabel: { |
| | | formatter: (val) => { |
| | | const newVal = moment(val).format('HH:mm'); |
| | | return newVal; |
| | | }, |
| | | showMaxLabel: true, |
| | | }, |
| | | }, |
| | | }); |
| | | }; |
| | | const multiCompareChange = (val) => { |
| | | if (!groupedValues) return; |
| | | if (val) { |
| | | handleMultiCompare(); |
| | | } else { |
| | | setNewOption(); |
| | | } |
| | | }; |
| | | //#endregion |
| | | </script> |
| | | <style scoped lang="scss"></style> |
¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="flex items-center"> |
| | | <div class="flex items-center space-x-1"> |
| | | <div |
| | | class="ywicon icon-pre" |
| | | :class="{ 'cursor-not-allowed': !offsetClickIsAllow, 'cursor-pointer': offsetClickIsAllow }" |
| | | @click="preDayClick" |
| | | ></div> |
| | | <el-date-picker |
| | | style="width: 240px" |
| | | v-model="dateValue" |
| | | type="daterange" |
| | | :start-placeholder="START_PLACEHOLDER" |
| | | :end-placeholder="END_PLACEHOLDER" |
| | | :value-format="valueFormat" |
| | | :format="DEFAULT_FORMATS_DATE" |
| | | :disabled-date="disabledDate" |
| | | :clearable="false" |
| | | :disabled="disabled" |
| | | > |
| | | <template v-for="(value, name) in $slots" #[name]="slotData"> |
| | | <slot :name="name" v-bind="slotData || {}"></slot> |
| | | </template> |
| | | </el-date-picker> |
| | | <div |
| | | class="ywicon icon-next" |
| | | :class="{ 'cursor-not-allowed': !offsetClickIsAllow, 'cursor-pointer': offsetClickIsAllow }" |
| | | @click="nextDayClick" |
| | | ></div> |
| | | </div> |
| | | |
| | | <div class="ml-2 inline-flex items-center space-x-2 text-[14px]"> |
| | | <div |
| | | @click="quickPickRangeClick(parseInt(item))" |
| | | class="border border-solid rounded-md px-2 cursor-pointer" |
| | | :class="{ 'bg-[#1677ff]': parseInt(item) === quickPickValue, 'text-white': parseInt(item) === quickPickValue }" |
| | | v-for="item in Object.keys(timeRangeEnumMapTitle)" |
| | | :key="item" |
| | | > |
| | | {{ timeRangeEnumMapTitle[item] }} |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup lang="ts"> |
| | | import { ElDatePicker } from 'element-plus'; |
| | | import { definePropType } from 'element-plus/es/utils/vue/props/runtime'; |
| | | import { ref, type PropType, computed, watch } from 'vue'; |
| | | import type { TimestampParam } from '../types'; |
| | | import type { TimeRangeEnum } from './types'; |
| | | import { timeRangeEnumMapTitle, timeRangeEnumMapValue } from './types'; |
| | | import { |
| | | CURRENT_DAY, |
| | | DEFAULT_FORMATS_DATE, |
| | | DEFAULT_FORMATS_TIME, |
| | | END_PLACEHOLDER, |
| | | RANGE_SEPARATOR, |
| | | START_PLACEHOLDER, |
| | | } from '/@/components/form/datepicker/constants'; |
| | | import { formatDate } from '/@/utils/formatTime'; |
| | | import moment from 'moment'; |
| | | |
| | | const valueFormat = DEFAULT_FORMATS_DATE + ' ' + DEFAULT_FORMATS_TIME; |
| | | const props = defineProps({ |
| | | data: { |
| | | type: Object as PropType<TimestampParam>, |
| | | }, |
| | | disabled: { |
| | | type: Boolean, |
| | | default: false, |
| | | }, |
| | | }); |
| | | const dateValue = defineModel({ |
| | | type: definePropType<[string, string]>(Array), |
| | | }); |
| | | const emit = defineEmits(['change']); |
| | | |
| | | /** |
| | | * éè¦å¯¹ dateValue æ ¼å¼åï¼dataValue ç»ææ¶é´ä¸æ¯23:59:59 |
| | | */ |
| | | const formatDateValue = computed({ |
| | | get: () => { |
| | | if (!dateValue.value) return null; |
| | | return [moment(dateValue.value[0]).format('YYYY-MM-DD 00:00:00'), moment(dateValue.value[1]).format('YYYY-MM-DD 23:59:59')] as [ |
| | | string, |
| | | string |
| | | ]; |
| | | }, |
| | | set: (value) => { |
| | | dateValue.value = value; |
| | | }, |
| | | }); |
| | | |
| | | const disabledDate = (date: Date) => { |
| | | return date > CURRENT_DAY; |
| | | }; |
| | | const quickPickValue = ref<TimeRangeEnum>(null); |
| | | const quickPickRangeClick = (val: TimeRangeEnum) => { |
| | | if (quickPickValue.value === val) return; |
| | | |
| | | quickPickValue.value = val; |
| | | formatDateValue.value = timeRangeEnumMapValue[val]().map((item) => formatDate(item)) as [string, string]; |
| | | }; |
| | | |
| | | const offsetClickIsAllow = computed(() => !!dateValue.value && !props.disabled); |
| | | const preDayClick = () => { |
| | | if (!dateValue.value) return; |
| | | dateValue.value[0] = moment(dateValue.value[0]).subtract(1, 'day').format('YYYY-MM-DD HH:mm:ss'); |
| | | dateValue.value = [...dateValue.value]; |
| | | }; |
| | | |
| | | const nextDayClick = () => { |
| | | if (!dateValue.value) return; |
| | | dateValue.value[1] = moment(dateValue.value[1]).add(1, 'day').format('YYYY-MM-DD HH:mm:ss'); |
| | | dateValue.value = [...dateValue.value]; |
| | | }; |
| | | |
| | | watch( |
| | | () => formatDateValue.value, |
| | | (val) => { |
| | | emit('change', val); |
| | | } |
| | | ); |
| | | </script> |
| | | <style scoped lang="scss"> |
| | | :deep(.el-date-editor .el-range__close-icon--hidden) { |
| | | display: none; |
| | | } |
| | | </style> |
¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="flex items-center"> |
| | | <el-input-number |
| | | placeholder="æå°å¼" |
| | | :style="{ |
| | | width: inputWidth, |
| | | }" |
| | | class="rounded-full" |
| | | v-model="yRange.min" |
| | | @input="(val) => numInput(val, 'min')" |
| | | :controls="false" |
| | | ></el-input-number> |
| | | <span class="bg-[#cdcdcd] h-[32px] inline-block flex-center">~</span> |
| | | <el-input-number |
| | | placeholder="æå¤§å¼" |
| | | :style="{ |
| | | width: inputWidth, |
| | | }" |
| | | class="round" |
| | | v-model="yRange.max" |
| | | :controls="false" |
| | | @input="(val) => numInput(val, 'max')" |
| | | ></el-input-number> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup lang="ts"> |
| | | import { watch, type PropType, ref } from 'vue'; |
| | | |
| | | const emit = defineEmits(['input']); |
| | | |
| | | const yRange = ref({ |
| | | min: null, |
| | | max: null, |
| | | }); |
| | | |
| | | const realYRange = ref({ |
| | | min:null, |
| | | max:null |
| | | }) |
| | | const inputWidth = '82px'; |
| | | |
| | | const numInput = (val: any, type: 'min' | 'max') => { |
| | | realYRange.value[type] = val; |
| | | emit('input', realYRange.value); |
| | | }; |
| | | </script> |
| | | <style scoped lang="scss"></style> |
¶Ô±ÈÐÂÎļþ |
| | |
| | | import { getRecentDate, getRecentDateRange } from '/@/utils/util'; |
| | | |
| | | export const enum TimeRangeEnum { |
| | | CurrentDay, |
| | | ThreeDay, |
| | | SevenDay, |
| | | } |
| | | |
| | | export const timeRangeEnumMapTitle = { |
| | | [TimeRangeEnum.CurrentDay]: '彿¥', |
| | | [TimeRangeEnum.ThreeDay]: 'è¿ä¸æ¥', |
| | | [TimeRangeEnum.SevenDay]: 'è¿ä¸æ¥', |
| | | }; |
| | | export const timeRangeEnumMapValue = { |
| | | [TimeRangeEnum.CurrentDay]: () => getRecentDateRange(1), |
| | | [TimeRangeEnum.ThreeDay]: () => getRecentDateRange(3), |
| | | [TimeRangeEnum.SevenDay]: () => getRecentDateRange(7), |
| | | }; |
¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <el-date-picker |
| | | v-model="dateValue" |
| | | type="daterange" |
| | | :range-separator="RANGE_SEPARATOR" |
| | | :start-placeholder="START_PLACEHOLDER" |
| | | :end-placeholder="END_PLACEHOLDER" |
| | | :shortcuts="shortcuts" |
| | | :value-format="valueFormat" |
| | | :disabled-date="disabledDate" |
| | | > |
| | | <template v-for="(value, name) in $slots" #[name]="slotData"> |
| | | <slot :name="name" v-bind="slotData || {}"></slot> |
| | | </template> |
| | | </el-date-picker> |
| | | </template> |
| | | |
| | | <script setup lang="ts"> |
| | | import { ElDatePicker } from 'element-plus'; |
| | | import { definePropType } from 'element-plus/es/utils/vue/props/runtime'; |
| | | import { CURRENT_DAY, DEFAULT_FORMATS_DATE, END_PLACEHOLDER, RANGE_SEPARATOR, SHORTCUTS, START_PLACEHOLDER } from './constants'; |
| | | |
| | | const props = defineProps({ |
| | | valueFormat: { |
| | | type: String, |
| | | default: DEFAULT_FORMATS_DATE, |
| | | }, |
| | | shortcuts: { |
| | | type: Array, |
| | | default: () => [], |
| | | // default: SHORTCUTS |
| | | }, |
| | | }); |
| | | |
| | | const dateValue = defineModel({ |
| | | type: definePropType<[string, string]>(Array), |
| | | }); |
| | | |
| | | const disabledDate = (date: Date) => { |
| | | return date > CURRENT_DAY; |
| | | }; |
| | | </script> |
| | | <style scoped lang="scss"></style> |
| | |
| | | * @description format å£åº¦ + ææ + å å¨ï¼"YYYY-mm-dd HH:MM:SS WWW QQQQ ZZZ" |
| | | * @returns è¿åæ¼æ¥åçæ¶é´å符串 |
| | | */ |
| | | export function formatDate(date: Date, format: string): string { |
| | | export function formatDate(date: Date, format = 'YYYY-mm-dd HH:MM:SS'): string { |
| | | let we = date.getDay(); // ææ |
| | | let z = getWeek(date); // å¨ |
| | | let qut = Math.floor((date.getMonth() + 3) / 3).toString(); // å£åº¦ |
| | |
| | | * æè¿ n 天ç startDateãendDate |
| | | * @param dates |
| | | */ |
| | | export const getRecentDateRange = (dates: number) => { |
| | | export const getRecentDateRange = (dates: number, includesCurrent = true) => { |
| | | dates = includesCurrent ? dates - 1 : dates; |
| | | // è·åå½åæ¥æ |
| | | const endDate = new Date(); |
| | | const startDate = new Date(); |
| | |
| | | host: '0.0.0.0', |
| | | port: env.VITE_PORT as unknown as number, |
| | | open: JSON.parse(env.VITE_OPEN), |
| | | hmr: true, |
| | | hmr: false, |
| | | }, |
| | | build: { |
| | | // outDir: 'dist/' + mode.mode, |