Skip to content

Commit

Permalink
Clean code and convert into TSX
Browse files Browse the repository at this point in the history
  • Loading branch information
sheepzh committed Jan 19, 2024
1 parent ea408f5 commit 6d54bb8
Show file tree
Hide file tree
Showing 103 changed files with 1,850 additions and 2,142 deletions.
4 changes: 4 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,14 @@
"Auths",
"clipboardy",
"countup",
"daterange",
"echarts",
"filemanager",
"Hengyang",
"Kanban",
"Popconfirm",
"vueuse",
"webtime",
"zrender"
],
"cSpell.ignorePaths": [
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
},
"dependencies": {
"@element-plus/icons-vue": "^2.3.1",
"@vueuse/core": "^10.7.2",
"axios": "^1.6.5",
"clipboardy": "^4.0.0",
"countup.js": "^2.8.0",
Expand All @@ -71,4 +72,4 @@
"engines": {
"node": ">=18"
}
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
/**
* Copyright (c) 2021 Hengyang Zhang
*
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/

import type { Ref, PropType, VNode } from "vue"
import type { Ref, PropType } from "vue"

import { ElOption, ElSelect, ElTag } from "element-plus"
import { ref, h, defineComponent } from "vue"
import { ref, defineComponent } from "vue"
import statService, { HostSet } from "@service/stat-service"
import siteService from "@service/site-service"
import { t } from "@app/locale"
Expand Down Expand Up @@ -45,15 +45,15 @@ async function handleRemoteSearch(queryStr: string, trendDomainOptions: Ref<time

const HOST_PLACEHOLDER = t(msg => msg.analysis.common.hostPlaceholder)

function keyOfHostInfo(option: timer.site.SiteKey): string {
function calcKey(option: timer.site.SiteKey): string {
const { merged, virtual, host } = option
let prefix = '_'
merged && (prefix = 'm')
virtual && (prefix = 'v')
return `${prefix}${host || ''}`
}

function hostInfoOfKey(key: string): timer.site.SiteKey {
function calcSite(key: string): timer.site.SiteKey {
if (!key?.length) return undefined
const prefix = key.charAt(0)
return { host: key.substring(1), merged: prefix === 'm', virtual: prefix === 'v' }
Expand All @@ -62,22 +62,18 @@ function hostInfoOfKey(key: string): timer.site.SiteKey {
const MERGED_TAG_TXT = t(msg => msg.analysis.common.merged)
const VIRTUAL_TAG_TXT = t(msg => msg.analysis.common.virtual)

const renderOptionTag = (tagLabel: string) => h('span',
{ style: { float: "right", height: "34px" } },
h(ElTag, { size: 'small' }, () => tagLabel)
)

function renderHostLabel({ host, merged, virtual, alias }: timer.site.SiteInfo): VNode[] {
const result = [
h('span', {}, host)
]
alias && result.push(
h(ElTag, { size: 'small', type: 'info' }, () => alias)
)
merged && result.push(renderOptionTag(MERGED_TAG_TXT))
virtual && result.push(renderOptionTag(VIRTUAL_TAG_TXT))
return result
}
const SiteOptionTag = defineComponent({
props: {
text: String,
},
setup: ({ text }) => {
return () => (
<span style={{ float: "right", height: "34px" }}>
<ElTag size="small">{text}</ElTag>
</span>
)
}
})

const _default = defineComponent({
props: {
Expand All @@ -91,48 +87,58 @@ const _default = defineComponent({
setup(props, ctx) {
const domainKey: Ref<string> = ref('')
const trendSearching: Ref<boolean> = ref(false)
const trendDomainOptions: Ref<timer.site.SiteKey[]> = ref([])
const trendDomainOptions: Ref<timer.site.SiteInfo[]> = ref([])
const defaultSite: timer.site.SiteKey = props.site
const timeFormat: Ref<timer.app.TimeFormat> = ref(props.timeFormat)

if (defaultSite) {
domainKey.value = keyOfHostInfo(defaultSite)
domainKey.value = calcKey(defaultSite)
trendDomainOptions.value.push(defaultSite)
}

function handleSiteChange() {
const siteInfo: timer.site.SiteInfo = hostInfoOfKey(domainKey.value)
const handleSiteChange = () => {
const siteInfo: timer.site.SiteInfo = calcSite(domainKey.value)
ctx.emit('siteChange', siteInfo)
}

return () => [
h(ElSelect, {
placeholder: HOST_PLACEHOLDER,
class: 'filter-item',
modelValue: domainKey.value,
filterable: true,
remote: true,
loading: trendSearching.value,
clearable: true,
remoteMethod: (query: string) => handleRemoteSearch(query, trendDomainOptions, trendSearching),
onChange: (key: string) => {
return () => <>
<ElSelect
placeholder={HOST_PLACEHOLDER}
class="filter-item"
modelValue={domainKey.value}
filterable
remote
loading={trendSearching.value}
clearable
remoteMethod={(query: string) => handleRemoteSearch(query, trendDomainOptions, trendSearching)}
onChange={(key: string) => {
domainKey.value = key
handleSiteChange()
},
onClear: () => {
}}
onClear={() => {
domainKey.value = undefined
handleSiteChange()
}
}, () => (trendDomainOptions.value || [])?.map(
siteInfo => h(ElOption, {
value: keyOfHostInfo(siteInfo),
label: labelOfHostInfo(siteInfo),
}, () => renderHostLabel(siteInfo))
)),
h(TimeFormatFilterItem, {
defaultValue: timeFormat.value,
onSelect: (newVal: timer.app.TimeFormat) => ctx.emit('timeFormatChange', timeFormat.value = newVal)
})
]
}}
>
{(trendDomainOptions.value || [])?.map(
site => (
<ElOption
value={calcKey(site)}
label={labelOfHostInfo(site)}
>
<span>{site.host}</span>
{site.alias && <ElTag size="small" type="info">{site.alias}</ElTag>}
{site.merged && <SiteOptionTag text={MERGED_TAG_TXT} />}
{site.virtual && <SiteOptionTag text={VIRTUAL_TAG_TXT} />}
</ElOption>
)
)}
</ElSelect>
<TimeFormatFilterItem
defaultValue={timeFormat.value}
onChange={val => ctx.emit("timeFormatChange", timeFormat.value = val)}
/>
</>
}
})

Expand Down
46 changes: 46 additions & 0 deletions src/app/components/Analysis/components/Summary/Site.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* Copyright (c) 2023 Hengyang Zhang
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/

import { Ref, defineComponent } from "vue"
import { computedAsync } from "@vueuse/core"
import { labelOfHostInfo } from "../../util"
import { t } from "@app/locale"
import { useAnalysisSite } from "../../context"
import siteService from "@service/site-service"

const EMPTY_DESC = t(msg => msg.analysis.common.emptyDesc)

function renderChildren(site: timer.site.SiteInfo) {
if (!site) return <h1 class="site-alias">{EMPTY_DESC}</h1>

const { iconUrl, alias } = site
const label = labelOfHostInfo(site)
const title: string = alias ? alias : label
const subtitle: string = alias ? label : undefined
return <>
{iconUrl && <img src={iconUrl} width={24} height={24} />}
<h1 class="site-alias">{title}</h1>
{subtitle && <p class="site-host">{subtitle}</p>}
</>
}

const computedSiteInfo = async (siteKey: timer.site.SiteKey): Promise<timer.site.SiteInfo> => {
if (!siteKey) return undefined
return await siteService.get(siteKey)
}

const _default = defineComponent(() => {
const site = useAnalysisSite()
const siteInfo: Ref<timer.site.SiteInfo> = computedAsync(() => computedSiteInfo(site.value))
return () => (
<div class="site-container">
{renderChildren(siteInfo.value)}
</div>
)
})

export default _default
77 changes: 77 additions & 0 deletions src/app/components/Analysis/components/Summary/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/**
* Copyright (c) 2023 Hengyang Zhang
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
import type { Ref } from "vue"

import { computed, defineComponent } from "vue"
import Site from "./Site"
import { KanbanIndicatorCell, KanbanCard, KanbanIndicatorRow } from "@app/components/common/kanban"
import "./summary.sass"
import { ElCol } from "element-plus"
import { t } from "@app/locale"
import { cvt2LocaleTime, periodFormatter } from "@app/util/time"
import { useAnalysisRows, useAnalysisSite, useAnalysisTimeFormat } from "../../context"

type Summary = {
focus: number
visit: number
day: number
firstDay?: string
}

function computeSummary(site: timer.site.SiteKey, rows: timer.stat.Row[]): Summary {
if (!site) return undefined

const summary: Summary = { focus: 0, visit: 0, day: 0 }
summary.firstDay = rows?.[0]?.date
rows?.forEach(({ focus, time: visit }) => {
summary.focus += focus
summary.visit += visit
focus && (summary.day += 1)
})
return summary
}

const DAYS_LABEL = t(msg => msg.analysis.summary.day)
const FOCUS_LABEL = t(msg => msg.analysis.common.focusTotal)
const VISIT_LABEL = t(msg => msg.analysis.common.visitTotal)

const _default = defineComponent(() => {
const site = useAnalysisSite()
const timeFormat = useAnalysisTimeFormat()
const rows = useAnalysisRows()

const summary: Ref<Summary> = computed(() => computeSummary(site.value, rows.value))

return () => (
<KanbanCard title={t(msg => msg.analysis.summary.title)}>
<KanbanIndicatorRow>
<ElCol span={6}>
<Site />
</ElCol>
<ElCol span={6}>
<KanbanIndicatorCell
mainName={DAYS_LABEL}
mainValue={summary.value?.day?.toString?.() || '-'}
subTips={msg => msg.analysis.summary.firstDay}
subValue={summary.value?.firstDay ? `@${cvt2LocaleTime(summary.value?.firstDay)}` : ''}
/>
</ElCol>
<ElCol span={6}>
<KanbanIndicatorCell
mainName={FOCUS_LABEL}
mainValue={summary.value?.focus === undefined ? '-' : periodFormatter(summary.value?.focus, timeFormat.value, false)}
/>
</ElCol>
<ElCol span={6}>
<KanbanIndicatorCell mainName={VISIT_LABEL} mainValue={summary.value?.visit?.toString?.() || '-'} />
</ElCol>
</KanbanIndicatorRow>
</KanbanCard>
)
})

export default _default
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
*/

import type { PropType, Ref } from "vue"
import type { DimensionEntry, ValueFormatter } from "@app/components/analysis/util"
import type { DimensionEntry, ValueFormatter } from "@app/components/Analysis/util"

import { defineComponent, h, watch, onMounted, ref } from "vue"
import { defineComponent, watch, onMounted, ref } from "vue"
import ChartWrapper from "./wrapper"

const _default = defineComponent({
Expand All @@ -20,7 +20,12 @@ const _default = defineComponent({
setup(props) {
const elRef: Ref<HTMLDivElement> = ref()
const wrapper: ChartWrapper = new ChartWrapper()
const render = () => wrapper.render({ entries: props.data, title: props.title, valueFormatter: props.valueFormatter })

const render = () => wrapper.render({
entries: props.data,
title: props.title,
valueFormatter: props.valueFormatter,
})

watch(() => props.data, render)
watch(() => props.valueFormatter, render)
Expand All @@ -30,7 +35,7 @@ const _default = defineComponent({
render()
})

return () => h('div', { class: 'analysis-trend-dimension-chart', ref: elRef })
return () => <div class="analysis-trend-dimension-chart" ref={elRef} />
}
})

Expand Down
Loading

0 comments on commit 6d54bb8

Please sign in to comment.