Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,4 @@
"engines": {
"node": ">=22"
}
}
}
41 changes: 30 additions & 11 deletions src/database/site-cate-database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ type Item = {
* Name
*/
n: string
/**
* Auto rules
*/
a?: string[]
}

type Items = Record<number, Item>
Expand Down Expand Up @@ -49,43 +53,58 @@ class SiteCateDatabase extends BaseDatabase {

async listAll(): Promise<timer.site.Cate[]> {
const items = await this.getItems()
return Object.entries(items).map(([id, { n = '' } = {}]) => {
return Object.entries(items).map(([id, { n = '', a } = {}]) => {
return {
id: parseInt(id),
name: n,
autoRules: a ?? [],
} satisfies timer.site.Cate
})
}

async add(name: string): Promise<timer.site.Cate> {
async add(name: string, autoRules: string[]): Promise<timer.site.Cate> {
const items = await this.getItems()
const existId = Object.entries(items).find(([_, v]) => v.n === name)?.[0]
if (existId) {
// Exist already
return { id: parseInt(existId), name }
return { id: parseInt(existId), name, autoRules }
}

const id = (Object.keys(items || {}).map(k => parseInt(k)).sort().reverse()?.[0] ?? 0) + 1
items[id] = { n: name || items[id]?.n }
items[id] = { n: name || items[id]?.n, a: autoRules }

await this.saveItems(items)
return { name, id }
return { name, id, autoRules }
}

async update(id: number, name: string): Promise<void> {
if (!name) return

private async updateWithReplacer(id: number, replacer: (exist: Item) => Item): Promise<void> {
const items = await this.getItems()
const existId = Object.entries(items).find(([_, v]) => v.n === name)?.[0]
const exist = items[id]
if (!exist) return

if (existId) {
const replaced = replacer(exist)

if (Object.entries(items).some(([vid, v]) => v.n === replaced.n && parseInt(vid) !== id)) {
// Name exist already
return
}

items[id] = { ...items[id] || {}, n: name }
items[id] = replaced
await this.saveItems(items)
}

async updateName(id: number, name: string): Promise<void> {
await this.updateWithReplacer(id, exist => ({ ...exist, n: name }))
}

async update(cate: timer.site.Cate): Promise<void> {
await this.updateWithReplacer(cate.id, exist => ({
...exist,
n: cate.name,
a: cate.autoRules,
}))
}

async importData(data: any): Promise<void> {
let toImport = data[KEY] as Items
// Not import
Expand Down
3 changes: 2 additions & 1 deletion src/i18n/message/app/site-manage-resource.json
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@
"relatedMsg": "This category has been associated with {siteCount} sites and cannot be deleted",
"removeConfirm": "Confirm to delete category: {category}?",
"batchChange": "Change categories",
"batchDisassociate": "Disassociate categories"
"batchDisassociate": "Disassociate categories",
"autoRules": "Auto-categorization rules"
},
"form": {
"emptyAlias": "Please enter site name",
Expand Down
1 change: 1 addition & 0 deletions src/i18n/message/app/site-manage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export type SiteManageMessage = {
batchChange: string
batchDisassociate: string
removeConfirm: string
autoRules: string
}
form: {
emptyAlias: string
Expand Down
9 changes: 8 additions & 1 deletion src/pages/app/components/Report/ReportTable/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,13 @@ const _default = defineComponent((_, ctx) => {
() => filter.siteMerge,
], () => table.value?.doLayout?.())

const handleCateChange = (key: timer.site.SiteKey, newCateId: number | undefined) => {
data.value?.list
?.filter(isSite)
?.filter(item => siteEqual(item.siteKey, key))
?.forEach(i => i.cateId = newCateId)
}

return () => (
<ContentCard>
<Flex gap={23} width="100%" height="100%" column>
Expand Down Expand Up @@ -138,7 +145,7 @@ const _default = defineComponent((_, ctx) => {
/>
</>}
{visible.value.group && <GroupColumn />}
{visible.value.cate && <CateColumn onChange={refresh} />}
{visible.value.cate && <CateColumn onChange={handleCateChange} />}
<TimeColumn dimension="focus" />
{runColVisible.value && <TimeColumn dimension="run" />}
<VisitColumn />
Expand Down
4 changes: 2 additions & 2 deletions src/pages/app/components/SiteManage/SiteManageTable/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Flex from "@pages/components/Flex"
import { removeIconUrl, saveSiteRunState } from '@service/site-service'
import { ElSwitch, ElTable, ElTableColumn, type RenderRowData } from "element-plus"
import { defineComponent } from "vue"
import CategoryEditable from "../../common/category/CategoryEditable"
import Category from "../../common/category/CategoryEditable"
import { useSiteManageTable } from '../useSiteManage'
import AliasColumn from "./column/AliasColumn"
import OperationColumn from "./column/OperationColumn"
Expand Down Expand Up @@ -70,7 +70,7 @@ const _default = defineComponent<{}>(() => {
minWidth={140}
align="center"
v-slots={({ row }: RenderRowData<timer.site.SiteInfo>) => (
<CategoryEditable siteKey={row} modelValue={row?.cate} onChange={val => row.cate = val} />
<Category siteKey={row} modelValue={row?.cate} onChange={val => row.cate = val} />
)}
/>
<ElTableColumn
Expand Down
106 changes: 106 additions & 0 deletions src/pages/app/components/common/category/CategoryDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@

import { t } from '@app/locale'
import { ElButton, ElDialog, ElForm, ElFormItem, ElInput, ElInputTag, ElMessage } from 'element-plus'
import { createVNode, defineComponent, reactive, ref, render } from 'vue'

type Props = {
cate?: timer.site.Cate
onClose?: NoArgCallback
onSave?: (cate: timer.site.Cate) => void
}

const Component = defineComponent<Props>(props => {
const visible = ref(true)
const formData = reactive<Partial<timer.site.Cate>>({ ...props.cate })

const handleConfirm = () => {

}

const handleRulesChange = (rules: string[] | undefined = []) => {
if (new Set(rules).size < rules.length) {
ElMessage.warning('Rules contain duplicated items')
} else {
formData.autoRules = rules
}
}

return () => (
<ElDialog
modelValue={visible.value}
title={props.cate ? t(msg => msg.button.modify) : t(msg => msg.button.create)}
width={400}
destroyOnClose
beforeClose={props.onClose}
closeOnClickModal={false}
v-slots={{
footer: () => <>
<ElButton onClick={props.onClose}>{t(msg => msg.button.cancel)}</ElButton>
<ElButton type="primary" onClick={handleConfirm}>
{t(msg => msg.button.confirm)}
</ElButton>
</>
}}
>
<ElForm model={formData} labelPosition='top'>
<ElFormItem label={t(msg => msg.siteManage.cate.name)} prop="name" required>
<ElInput v-model={formData.name} />
</ElFormItem>
<ElFormItem label={t(msg => msg.siteManage.cate.autoRules)} prop="autoRules">
<ElInputTag
placeholder='**.google.com, *.example.com'
modelValue={formData.autoRules}
trigger='Space'
onChange={handleRulesChange}
/>
</ElFormItem>
</ElForm>
</ElDialog>
)
}, { props: ['cate', 'onClose', 'onSave'] })

interface DialogOptions {
cate?: timer.site.Cate
}

function open(options: DialogOptions = {}): Promise<timer.site.Cate> {
const { cate } = options

return new Promise<timer.site.Cate>((resolve, reject) => {
const container = document.createElement('div')
document.body.appendChild(container)

const cleanup = () => {
render(null, container)
document.body.removeChild(container)
}

const vnode = createVNode(Component, {
cate,
onClose: async (data: timer.site.Cate) => {
try {
resolve(data)
} catch (error) {
reject(error)
} finally {
cleanup()
}
},
onCancel: () => {
reject()
cleanup()
}
})

render(vnode, container)
})
}

type CategoryDialogDef = typeof Component & {
open: typeof open
}

const CategoryDialog = Component as CategoryDialogDef
CategoryDialog.open = open

export default CategoryDialog
14 changes: 3 additions & 11 deletions src/pages/app/components/common/category/CategoryEditable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import Flex from "@pages/components/Flex"
import { saveSiteCate } from '@service/site-service'
import { supportCategory } from "@util/site"
import { ElIcon, ElTag } from "element-plus"
import { computed, defineComponent, nextTick, ref } from "vue"
import CategorySelect, { CategorySelectInstance } from "./CategorySelect"
import { computed, defineComponent } from "vue"
import CategorySelect from "./CategorySelect"

type Props = ModelValue<number | undefined> & {
siteKey: timer.site.SiteKey
Expand Down Expand Up @@ -34,18 +34,10 @@ const CategoryEditable = defineComponent<Props>(props => {
},
})

const handleEditClick = () => {
openEditing()
nextTick(() => selectRef.value?.openOptions?.())
}

const selectRef = ref<CategorySelectInstance>()

return () => supportCategory(props.siteKey) ?
<Flex width="100%" height="100%" justify="center">
{editing.value ?
<CategorySelect
ref={selectRef}
size="small"
width="100px"
modelValue={props.modelValue}
Expand All @@ -63,7 +55,7 @@ const CategoryEditable = defineComponent<Props>(props => {
{current.value.name}
</ElTag>
}
<Flex align="center" onClick={handleEditClick}>
<Flex align="center" onClick={openEditing}>
<ElIcon style={{ cursor: 'pointer' }}>
<Edit />
</ElIcon>
Expand Down
Loading
Loading