Skip to content

Commit 330f5fc

Browse files
author
sheepzh
committed
Support to modify the daily time limit (#176)
1 parent b922a4a commit 330f5fc

File tree

19 files changed

+413
-226
lines changed

19 files changed

+413
-226
lines changed

src/app/components/dashboard/components/top-k-visit.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import TooltipComponent from "@echarts/component/tooltip"
1818

1919
use([PieChart, TitleComponent, TooltipComponent])
2020

21-
import timerService, { SortDirect } from "@service/timer-service"
21+
import timerService from "@service/timer-service"
2222
import { MILL_PER_DAY } from "@util/time"
2323
import { ElLoading } from "element-plus"
2424
import { defineComponent, h, onMounted, ref } from "vue"
@@ -113,7 +113,7 @@ const _default = defineComponent({
113113
const query: TimerQueryParam = {
114114
date: [startTime, now],
115115
sort: "time",
116-
sortOrder: SortDirect.DESC,
116+
sortOrder: 'DESC',
117117
mergeDate: true,
118118
}
119119
const top: timer.stat.Row[] = (await timerService.selectByPage(query, { num: 1, size: TOP_NUM }, { alias: true })).list

src/app/components/limit/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ const _default = defineComponent({
4343
onlyEnabled.value = option.onlyEnabled
4444
queryData()
4545
},
46-
onCreate: () => modify.value?.show?.()
46+
onCreate: () => modify.value?.create?.()
4747
}),
4848
content: () => [
4949
h(LimitTable, {
@@ -54,6 +54,9 @@ const _default = defineComponent({
5454
await limitService.remove(row)
5555
ElMessage.success(t(msg => msg.limit.message.deleted))
5656
queryData()
57+
},
58+
async onModify(row: timer.limit.Item) {
59+
modify.value?.modify?.(row)
5760
}
5861
}),
5962
h(Modify, {
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
export function parseUrl(url: string): UrlInfo {
2+
let protocol: Protocol = '*://'
3+
4+
url = decodeURI(url)?.trim()
5+
if (url.startsWith('http://')) {
6+
protocol = 'http://'
7+
url = url.substring(protocol.length)
8+
} else if (url.startsWith('https://')) {
9+
protocol = 'https://'
10+
url = url.substring(protocol.length)
11+
} else if (url.startsWith('*://')) {
12+
protocol = '*://'
13+
url = url.substring(protocol.length)
14+
}
15+
return { protocol, url }
16+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/**
2+
* Copyright (c) 2023 Hengyang Zhang
3+
*
4+
* This software is released under the MIT License.
5+
* https://opensource.org/licenses/MIT
6+
*/
7+
8+
declare type FormInfo = {
9+
/**
10+
* Time / seconds
11+
*/
12+
timeLimit: number
13+
/**
14+
* Protocol + path
15+
*/
16+
condition: string
17+
}
18+
19+
declare type UrlPart = {
20+
/**
21+
* The origin part text
22+
*/
23+
origin: string
24+
/**
25+
* Whether to replace with wildcard
26+
*/
27+
ignored: boolean
28+
}
29+
30+
declare type UrlInfo = {
31+
protocol: Protocol
32+
url: string
33+
}
34+
35+
/**
36+
* The protocol of rule host
37+
*/
38+
declare type Protocol =
39+
| 'http://'
40+
| 'https://'
41+
| '*://'

src/app/components/limit/modify/form/index.ts

Lines changed: 86 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -6,74 +6,102 @@
66
*/
77

88
import { ElForm } from "element-plus"
9-
import { computed, ComputedRef, defineComponent, h, ref, Ref, SetupContext } from "vue"
9+
import { computed, ComputedRef, defineComponent, h, reactive, ref, Ref, UnwrapRef } from "vue"
1010
import '../style/el-input'
11-
import pathEdit from "./path-edit"
12-
import timeFormItem from "./time-limit"
13-
import urlFormItem, { FormUrlProps, Protocol } from "./url"
14-
import UrlPathItem from "./url-path-item"
11+
import LimitPathEdit from "./path-edit"
12+
import LimitUrlFormItem from "./url"
13+
import LimitTimeFormItem from "./time-limit"
14+
import { parseUrl } from "./common"
1515

16-
// Limited time
17-
const hourRef: Ref<number> = ref()
18-
const minuteRef: Ref<number> = ref()
19-
const secondRef: Ref<number> = ref()
20-
21-
// Limited url
22-
const protocolRef: Ref<string> = ref()
23-
const pathItemsRef: Ref<UrlPathItem[]> = ref([])
24-
const urlRef: ComputedRef<string> = computed(() => pathItemsRef.value.map(i => i.toString()).join('/'))
16+
function computeFormInfo(urlInfo: UrlInfo, timeRef: Ref<number>): FormInfo {
17+
const { url, protocol } = urlInfo
18+
const result: FormInfo = {
19+
timeLimit: timeRef.value || 0,
20+
condition: url ? protocol + url : ''
21+
}
22+
return result
23+
}
2524

26-
const timeProps = { hourRef, minuteRef, secondRef }
27-
const urlProps: FormUrlProps = { protocolRef, pathItemsRef, urlRef }
28-
const pathEditProps = { pathItemsRef }
29-
const render = () => h(ElForm,
30-
{ labelWidth: '100px' },
31-
() => [timeFormItem(timeProps), urlFormItem(urlProps), pathEdit(pathEditProps)]
32-
)
25+
function init(timeRef: Ref<number>, urlInfo: UrlInfo) {
26+
// 1 hour
27+
timeRef.value = 3600
28+
urlInfo.protocol = '*://'
29+
urlInfo.url = ''
30+
}
3331

34-
function init() {
35-
hourRef.value = 1
36-
minuteRef.value = undefined
37-
secondRef.value = undefined
38-
protocolRef.value = Protocol.ALL
39-
pathItemsRef.value = []
32+
function parseRow(row: timer.limit.Item, timeRef: Ref<number>, urlInfo: UrlInfo) {
33+
const { cond, time } = row
34+
timeRef.value = time || 0
35+
const { protocol, url } = parseUrl(cond)
36+
urlInfo.url = url
37+
urlInfo.protocol = protocol
4038
}
4139

42-
init()
40+
const _default = defineComponent({
41+
name: "LimitForm",
42+
setup(_, ctx) {
43+
// Limited time
44+
const timeRef: Ref<number> = ref()
45+
// Limited url
46+
const urlInfo: UnwrapRef<UrlInfo> = reactive({
47+
protocol: undefined,
48+
url: undefined
49+
})
50+
const editMode: Ref<Mode> = ref('create')
51+
init(timeRef, urlInfo)
4352

44-
export type FormData = {
45-
/**
46-
* Time / seconds
47-
*/
48-
timeLimit: number
49-
/**
50-
* Protocol + url
51-
*/
52-
url: string
53-
}
53+
const formInfo: ComputedRef<FormInfo> = computed(() => computeFormInfo(urlInfo, timeRef))
54+
const canEditUrl: ComputedRef<boolean> = computed(() => editMode.value === 'create')
55+
const pathEditRef: Ref = ref()
5456

55-
const handleGetData = () => {
56-
let timeLimit = 0
57-
timeLimit += (hourRef.value || 0) * 3600
58-
timeLimit += (minuteRef.value || 0) * 60
59-
timeLimit += (secondRef.value || 0)
60-
const url = urlRef.value || ''
61-
const protocol = protocolRef.value
62-
const result: FormData = {
63-
timeLimit,
64-
url: url ? protocol + url : ''
65-
}
66-
return result
67-
}
57+
function setUrl(newUrl: string) {
58+
urlInfo.url = newUrl
59+
pathEditRef?.value?.forceUpdateUrl?.(newUrl)
60+
}
6861

69-
const exposeOption = {
70-
getData: () => handleGetData(),
71-
clean: () => init()
72-
}
62+
ctx.expose({
63+
getData: () => formInfo.value,
64+
clean: () => {
65+
editMode.value = 'create'
66+
init(timeRef, urlInfo)
67+
},
68+
modify: (row: timer.limit.Item) => {
69+
editMode.value = 'modify'
70+
parseRow(row, timeRef, urlInfo)
71+
setUrl(urlInfo.url)
72+
},
73+
})
7374

74-
const _default = defineComponent({
75-
setup: (_, context: SetupContext) => context.expose(exposeOption),
76-
render
75+
return () => h(ElForm,
76+
{ labelWidth: 120 },
77+
() => {
78+
const items = [
79+
h(LimitTimeFormItem, {
80+
modelValue: timeRef.value,
81+
onChange: (newVal: number) => timeRef.value = newVal
82+
}),
83+
h(LimitUrlFormItem, {
84+
url: urlInfo.url,
85+
protocol: urlInfo.protocol,
86+
disabled: !canEditUrl.value,
87+
onUrlChange: (newUrl: string) => setUrl(newUrl),
88+
onProtocolChange: (newProtocol: Protocol) => urlInfo.protocol = newProtocol
89+
}),
90+
]
91+
canEditUrl.value && items.push(
92+
h(LimitPathEdit, {
93+
ref: pathEditRef,
94+
disabled: editMode.value === 'modify',
95+
url: urlInfo.url,
96+
onUrlChange: (newVal: string) => {
97+
urlInfo.url = newVal
98+
}
99+
})
100+
)
101+
return items
102+
}
103+
)
104+
}
77105
})
78106

79107
export default _default

src/app/components/limit/modify/form/path-edit.ts

Lines changed: 54 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,19 @@
66
*/
77

88
import { ElSwitch, ElTag, ElTooltip } from "element-plus"
9-
import { h, Ref, VNode } from "vue"
9+
import { defineComponent, h, ref, Ref, VNode } from "vue"
1010
import { t } from "@app/locale"
11-
import UrlPathItem from "./url-path-item"
1211

13-
type _Props = {
14-
pathItemsRef: Ref<UrlPathItem[]>
15-
}
12+
const switchStyle: Partial<CSSStyleDeclaration> = { marginRight: '2px' }
1613

17-
const switchStyle = { marginRight: '2px' }
18-
const tagContent = (item: UrlPathItem, index: number, arr: UrlPathItem[]) => {
14+
const tagContent = (item: UrlPart, index: number, callback: Function) => {
1915
const result: VNode[] = []
2016
if (!!index) {
2117
const modelValue = item.ignored
22-
const onChange = (val: boolean) => item.ignored = val
18+
const onChange = (val: boolean) => {
19+
item.ignored = val
20+
callback?.()
21+
}
2322
const switchNode = h(ElSwitch, { style: switchStyle, modelValue, onChange })
2423

2524
const tooltipNode = h(ElTooltip, { content: t(msg => msg.limit.useWildcard) }, { default: () => switchNode })
@@ -29,20 +28,22 @@ const tagContent = (item: UrlPathItem, index: number, arr: UrlPathItem[]) => {
2928
return result
3029
}
3130

32-
const tabStyle = { marginBottom: '5px' }
33-
const item2Tag = (item: UrlPathItem, index: number, arr: UrlPathItem[]) => {
31+
const tabStyle: Partial<CSSStyleDeclaration> = {
32+
marginBottom: '5px',
33+
marginRight: '0',
34+
}
35+
36+
const item2Tag = (item: UrlPart, index: number, arr: UrlPart[], onChange: Function) => {
3437
const isNotHost: boolean = !!index
35-
return h(ElTag,
36-
{
37-
type: isNotHost ? '' : 'info',
38-
closable: isNotHost,
39-
onClose: () => arr.splice(index),
40-
style: tabStyle
38+
return h(ElTag, {
39+
type: isNotHost ? '' : 'info',
40+
closable: isNotHost,
41+
onClose: () => {
42+
arr.splice(index)
43+
onChange?.()
4144
},
42-
{
43-
default: () => tagContent(item, index, arr)
44-
}
45-
)
45+
style: tabStyle
46+
}, () => tagContent(item, index, onChange))
4647
}
4748
const combineStyle = {
4849
fontSize: '14px',
@@ -55,6 +56,38 @@ const combineTags = (arr: VNode[], current: VNode) => {
5556
return arr
5657
}
5758

58-
const _default = (props: _Props) => h('div', {}, props.pathItemsRef.value.map(item2Tag).reduce(combineTags, []))
59+
function url2PathItems(url: string): UrlPart[] {
60+
return url.split('/').filter(path => path).map(path => ({ origin: path, ignored: path === '*' }))
61+
}
62+
63+
function pathItems2Url(pathItems: UrlPart[]): string {
64+
return pathItems.map(i => i.ignored ? '*' : i.origin || '').join('/')
65+
}
66+
67+
const _default = defineComponent({
68+
name: 'LimitPathEdit',
69+
props: {
70+
url: {
71+
type: String,
72+
required: false,
73+
defaultValue: ''
74+
},
75+
},
76+
emits: ['urlChange'],
77+
setup(props, ctx) {
78+
const url = props.url
79+
const items: Ref<UrlPart[]> = ref(url2PathItems(url))
80+
ctx.expose({
81+
forceUpdateUrl(url: string) {
82+
items.value = url2PathItems(url)
83+
}
84+
})
85+
const handleUrlChange = () => ctx.emit('urlChange', pathItems2Url(items.value))
86+
return () => h('div', {}, items.value
87+
.map((item, index, arr) => item2Tag(item, index, arr, handleUrlChange))
88+
.reduce(combineTags, [])
89+
)
90+
}
91+
})
5992

6093
export default _default

0 commit comments

Comments
 (0)