Skip to content

Commit

Permalink
Optimize verification of limit (#258)
Browse files Browse the repository at this point in the history
  • Loading branch information
sheepzh committed Jan 19, 2024
1 parent 6d54bb8 commit 7418e0d
Show file tree
Hide file tree
Showing 59 changed files with 979 additions and 1,092 deletions.
71 changes: 71 additions & 0 deletions src/app/components/Limit/LimitFilter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/**
* Copyright (c) 2021 Hengyang Zhang
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/

import { Operation, Plus, SetUp } from "@element-plus/icons-vue"
import { Ref, defineComponent, watch, ref } from "vue"
import InputFilterItem from "@app/components/common/input-filter-item"
import SwitchFilterItem from "@app/components/common/switch-filter-item"
import ButtonFilterItem from "@app/components/common/button-filter-item"
import { t } from "@app/locale"
import { getAppPageUrl } from "@util/constant/url"
import { OPTION_ROUTE } from "@app/router/constants"
import { createTabAfterCurrent } from "@api/chrome/tab"

const optionPageUrl = getAppPageUrl(false, OPTION_ROUTE, { i: 'dailyLimit' })

const _default = defineComponent({
props: {
url: String,
onlyEnabled: Boolean
},
emits: {
create: () => true,
change: (_option: LimitFilterOption) => true,
test: () => true,
},
setup(props, ctx) {
const url: Ref<string> = ref(props.url)
const onlyEnabled: Ref<boolean> = ref(props.onlyEnabled)
watch([url, onlyEnabled], () => ctx.emit("change", {
url: url.value,
onlyEnabled: onlyEnabled.value
}))
return () => <>
<InputFilterItem
defaultValue={url.value}
placeholder={t(msg => msg.limit.conditionFilter)}
onSearch={val => url.value = val}
/>
<SwitchFilterItem
historyName="onlyEnabled"
label={t(msg => msg.limit.filterDisabled)}
defaultValue={onlyEnabled.value}
onChange={val => onlyEnabled.value = val}
/>
<ButtonFilterItem
text={t(msg => msg.limit.button.test)}
type="primary"
icon={<Operation />}
onClick={() => ctx.emit("test")}
/>
<ButtonFilterItem
text={t(msg => msg.limit.button.option)}
icon={<SetUp />}
type="primary"
onClick={() => createTabAfterCurrent(optionPageUrl)}
/>
<ButtonFilterItem
text={t(msg => msg.button.create)}
type="success"
icon={<Plus />}
onClick={() => ctx.emit("create")}
/>
</>
}
})

export default _default
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
/**
* Copyright (c) 2021 Hengyang Zhang
*
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/

import { ElSwitch, ElTag, ElTooltip } from "element-plus"
import { defineComponent, h, PropType, reactive, ref, VNode, watch } from "vue"
import { defineComponent, PropType, reactive, ref, StyleValue, VNode, watch } from "vue"
import { t } from "@app/locale"
import { UrlPart } from "./common"

const switchStyle: Partial<CSSStyleDeclaration> = { marginRight: '2px' }
const switchStyle: StyleValue = { marginRight: '2px' }

const tabStyle: Partial<CSSStyleDeclaration> = {
const tabStyle: StyleValue = {
marginBottom: '5px',
marginRight: '0',
}
Expand All @@ -29,42 +29,29 @@ const ItemTag = defineComponent({
const myIgnored = ref(ignored)

watch(myIgnored, () => ctx.emit("change", { ignored: myIgnored.value, origin }))

return () => h(ElTag, {
type: 'info',
closable: true,
onClose: () => ctx.emit("close"),
style: tabStyle
}, () => [
h(ElTooltip, {
content: t(msg => msg.limit.useWildcard)
}, {
default: () => h(ElSwitch, {
style: switchStyle,
modelValue: myIgnored.value,
onChange: (newVal: boolean) => myIgnored.value = newVal
})
}),
h('span', {}, origin),
])
return () => (
<ElTag type="info" closable onClose={() => ctx.emit("close")} style={tabStyle}>
<ElTooltip content={t(msg => msg.limit.useWildcard)}>
<ElSwitch
style={switchStyle}
modelValue={myIgnored.value}
onChange={val => myIgnored.value = !!val}
/>
</ElTooltip>
<span>{origin}</span>
</ElTag>
)
}
})

const item2Tag = (item: UrlPart, index: number, arr: UrlPart[]) => {
const isNotHost: boolean = !!index
return isNotHost
? h(ItemTag, { part: item, onClose: () => arr.splice(index), onChange: p => arr[index] = p })
: h(ElTag, { style: tabStyle }, () => h('span', item.origin))
}

const combineStyle = {
const combineStyle: StyleValue = {
fontSize: '14px',
margin: '0 2px',
...tabStyle
}

const combineTags = (arr: VNode[], current: VNode) => {
arr.length && arr.push(h('span', { style: combineStyle }, '/'))
arr.length && arr.push(<span style={combineStyle}>/</span>)
arr.push(current)
return arr
}
Expand All @@ -82,7 +69,6 @@ export type PathEditInstance = {
}

const _default = defineComponent({
name: 'LimitPathEdit',
props: {
url: {
type: String,
Expand All @@ -107,9 +93,15 @@ const _default = defineComponent({

ctx.expose(instance)

return () => h('div', {}, items
.map((item, index, arr) => item2Tag(item, index, arr))
.reduce(combineTags, [])
return () => (
<div>
{
items.map((item, idx, arr) => idx
? <ItemTag part={item} onClose={() => arr.splice(idx)} onChange={p => arr[idx] = p} />
: <ElTag style={tabStyle}> <span>{item.origin}</span> </ElTag>
).reduce(combineTags, [])
}
</div>
)
}
})
Expand Down
120 changes: 120 additions & 0 deletions src/app/components/Limit/LimitModify/Sop/LimitPeriodFormItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/**
* Copyright (c) 2021 Hengyang Zhang
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/

import { t } from "@app/locale"
import { Check, Close, Plus } from "@element-plus/icons-vue"
import { ElButton, ElFormItem, ElTag, ElTimePicker } from "element-plus"
import { PropType, defineComponent, reactive, ref } from "vue"
import { checkImpact, dateMinute2Idx, mergePeriod, period2Str } from "@util/limit"

const range2Period = (range: [Date, Date]): [number, number] => {
const start = range?.[0]
const end = range?.[1]
if (start === undefined || end === undefined) {
return undefined
}
const startIdx = dateMinute2Idx(start)
const endIdx = dateMinute2Idx(end)
return [Math.min(startIdx, endIdx), Math.max(startIdx, endIdx)]
}

const PeriodInput = defineComponent({
emits: {
close: () => true,
save: (_p: timer.limit.Period) => true,
},
setup(_, ctx) {
const range = ref<[Date, Date]>()
const handleSave = () => {
const val = range2Period(range.value)
val && ctx.emit("save", val)
}
return () => (
<div class="limit-period-input">
<ElTimePicker
modelValue={range.value}
onUpdate:modelValue={val => range.value = val}
popperClass="limit-period-time-picker-popper"
isRange
rangeSeparator="-"
format="HH:mm"
clearable={false}
/>
<ElButton icon={<Close />} onClick={() => ctx.emit("close")} />
<ElButton icon={<Check />} onClick={handleSave} />
</div>
)
}
})

const insertPeriods = (periods: timer.limit.Period[], toInsert: timer.limit.Period) => {
if (!toInsert || !periods) return
let len = periods.length
if (!len) {
periods.push(toInsert)
return
}
for (let i = 0; i < len; i++) {
const pre = periods[i]
const next = periods[i + 1]
if (checkImpact(pre, toInsert)) {
mergePeriod(pre, toInsert)
if (checkImpact(pre, next)) {
mergePeriod(pre, next)
periods.splice(i + 1, 1)
}
return
}
if (checkImpact(toInsert, next)) {
mergePeriod(next, toInsert)
return
}
}
// Append
periods.push(toInsert)
periods.sort((a, b) => a[0] - b[0])
}

const _default = defineComponent({
props: {
modelValue: Array as PropType<timer.limit.Period[]>
},
setup({ modelValue }) {
const periods = reactive(modelValue || [])
const periodEditing = ref(false)

return () => (
<ElFormItem label={t(msg => msg.limit.item.period)}>
<div class="period-form-item-container">
{periods?.map((p, idx) =>
<ElTag
closable
size="small"
onClose={() => periods.splice(idx, 1)}
>
{period2Str(p)}
</ElTag>
)}
{periodEditing.value
? <PeriodInput
onClose={() => periodEditing.value = false}
onSave={p => {
insertPeriods(periods, p)
periodEditing.value = false
}}
/>
: <ElButton icon={<Plus />} size="small" onClick={() => periodEditing.value = true}>
{t(msg => msg.button.create)}
</ElButton>
}
</div>
</ElFormItem>
)
}
})

export default _default
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/**
* Copyright (c) 2021 Hengyang Zhang
*
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
Expand Down
87 changes: 87 additions & 0 deletions src/app/components/Limit/LimitModify/Sop/Step1.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/**
* Copyright (c) 2021 Hengyang Zhang
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/

import { t } from "@app/locale"
import { Close, Right } from "@element-plus/icons-vue"
import { ElButton, ElForm, ElMessage } from "element-plus"
import { Ref, defineComponent, ref, watch } from "vue"
import { Protocol, parseUrl } from "./common"
import LimitUrlFormItem from "./url"
import LimitPathEdit, { PathEditInstance } from "./LimitPathEdit"

const _default = defineComponent({
props: {
defaultValue: {
type: String,
required: true,
},
disabled: Boolean,
},
emits: {
cancel: () => true,
next: (_cond: string) => true,
},
setup(props, ctx) {
const { protocol: defaultProtocol, url: defaultUrl } = parseUrl(props.defaultValue)
const protocol: Ref<Protocol> = ref(defaultProtocol)
const pathEdit: Ref<PathEditInstance> = ref()
const url: Ref<string> = ref(defaultUrl)

watch(() => props.defaultValue, () => {
const { protocol: newProtocol, url: newUrl } = parseUrl(props.defaultValue)
protocol.value = newProtocol
url.value = newUrl
})

const handleNext = () => {
let cond = props.defaultValue
if (!props.disabled) {
const urlVal = url.value?.trim?.()
if (!urlVal) {
return ElMessage.error(t(msg => msg.limit.message.noUrl))
}
cond = urlVal ? protocol.value + urlVal : ''
}
ctx.emit("next", cond)
}

return () => <>
<ElForm labelWidth={180} labelPosition="left">
<LimitUrlFormItem
url={url.value}
protocol={protocol.value}
disabled={props.disabled}
onUrlChange={val => {
url.value = val
pathEdit.value?.updateUrl?.(val)
}}
onProtocolChange={val => protocol.value = val}
/>
</ElForm>
<LimitPathEdit
v-show={!props.disabled}
ref={pathEdit}
url={url.value}
onUrlChange={val => url.value = val}
/>
<div class="sop-footer">
<ElButton type="info" icon={<Close />} onClick={() => ctx.emit("cancel")}>
{t(msg => msg.button.cancel)}
</ElButton>
<ElButton
type="primary"
icon={<Right />}
onClick={handleNext}
>
{t(msg => msg.button.next)}
</ElButton>
</div>
</>
}
})

export default _default
Loading

0 comments on commit 7418e0d

Please sign in to comment.