Browse Source

fix lint

pull/395/head
Goxiaoy 3 years ago
parent
commit
6c85a47dee
  1. 44
      admin/.eslintrc.js
  2. 4
      admin/package.json
  3. 4
      admin/src/components.d.ts
  4. 2
      admin/src/components/Screenfull/index.vue
  5. 10
      admin/src/layout/aside.vue
  6. 10
      admin/src/layout/components/content.vue
  7. 22
      admin/src/layout/components/header.vue
  8. 4
      admin/src/layout/components/sidebar.vue
  9. 10
      admin/src/layout/index.vue
  10. 18
      admin/src/main.ts
  11. 1
      admin/src/permission.ts
  12. 16
      admin/src/store/modules/layout.ts
  13. 2
      admin/src/type/shim.vue.d.ts
  14. 10
      admin/src/utils/util.ts
  15. 238
      admin/src/views/Dashboard/GlobalTransactions/AllTransactions.vue
  16. 15
      admin/src/views/Dashboard/GlobalTransactions/_Components/DialogTransactionDetail.vue
  17. 185
      admin/src/views/Dashboard/KVPairs/Topics.vue
  18. 106
      admin/src/views/Dashboard/KVPairs/_Components/DialogTopicDetail.vue
  19. 96
      admin/src/views/Dashboard/KVPairs/_Components/DialogTopicSubscribe.vue
  20. 10
      admin/tailwind.config.js
  21. 130
      admin/vite.config.ts

44
admin/.eslintrc.js

@ -1,32 +1,32 @@
module.exports = {
parser: "vue-eslint-parser",
"env": {
"browser": true,
"es2021": true
parser: 'vue-eslint-parser',
env: {
browser: true,
es2021: true
},
"extends": [
"plugin:@typescript-eslint/recommended",
"plugin:vue/vue3-recommended"
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:vue/vue3-recommended'
],
"parserOptions": {
"parser": "@typescript-eslint/parser",
"sourceType": "module",
parserOptions: {
parser: '@typescript-eslint/parser',
sourceType: 'module',
ecmaFeature: {
jsx: true,
tsx: true
}
},
"plugins": [
"@typescript-eslint"
plugins: [
'@typescript-eslint'
],
"rules": {
rules: {
'vue/max-attributes-per-line': ['error', {
singleline: 10,
multiline: {
max: 1,
allowFirstLine: false
max: 1
}
}],
'vue/multi-word-component-names': 0,
'vue/singleline-html-element-content-newline': 'off',
'vue/multiline-html-element-content-newline': 'off',
'vue/html-indent': ['error', 4],
@ -35,7 +35,9 @@ module.exports = {
quotes: ['error', 'single'], // 单引号
// 'vue/html-quotes': ['error', 'single'],
semi: ['error', 'never'], // 禁止使用分号
'space-infix-ops': ['error', { int32Hint: false }], // 要求操作符周围有空格
'space-infix-ops': ['error', {
int32Hint: false
}], // 要求操作符周围有空格
'no-multi-spaces': 'error', // 禁止多个空格
'no-whitespace-before-property': 'error', // 禁止在属性前使用空格
'space-before-blocks': 'error', // 在块之前强制保持一致的间距
@ -48,8 +50,8 @@ module.exports = {
array: true,
object: true
}, {
enforceForRenamedProperties: false
}],
enforceForRenamedProperties: false
}],
'comma-dangle': ['error', 'never'], // 最后一个属性不允许有逗号
'arrow-spacing': 'error', // 箭头函数空格
'prefer-template': 'error',
@ -59,10 +61,12 @@ module.exports = {
'no-unneeded-ternary': 'error', // 禁止可以表达为更简单结构的三元操作符
'no-restricted-syntax': ['error', 'WithStatement', 'BinaryExpression[operator="in"]'], // 禁止with/in语句
'no-lonely-if': 'error', // 禁止 if 语句作为唯一语句出现在 else 语句块中
'newline-per-chained-call': ['error', { ignoreChainWithDepth: 2 }], // 要求方法链中每个调用都有一个换行符
'newline-per-chained-call': ['error', {
ignoreChainWithDepth: 2
}], // 要求方法链中每个调用都有一个换行符
// 路径别名设置
'no-submodule-imports': ['off', '/@'],
'no-implicit-dependencies': ['off', ['/@']],
'@typescript-eslint/no-explicit-any': 'off' // 类型可以使用any
}
}
}

4
admin/package.json

@ -5,7 +5,9 @@
"scripts": {
"dev": "vite",
"build": "vite build && echo > dist/placeholder",
"preview": "vite preview"
"preview": "vite preview",
"lint": "eslint --ext .tsx,.ts,vue src/",
"lint:fix": "eslint --ext .tsx,.ts,vue src/ --fix"
},
"dependencies": {
"ant-design-vue": "^3.1.1",

4
admin/src/components.d.ts

@ -5,7 +5,6 @@ import '@vue/runtime-core'
declare module '@vue/runtime-core' {
export interface GlobalComponents {
AAlert: typeof import('ant-design-vue/es')['Alert']
ABreadcrumb: typeof import('ant-design-vue/es')['Breadcrumb']
ABreadcrumbItem: typeof import('ant-design-vue/es')['BreadcrumbItem']
AButton: typeof import('ant-design-vue/es')['Button']
@ -19,11 +18,8 @@ declare module '@vue/runtime-core' {
ALayoutSider: typeof import('ant-design-vue/es')['LayoutSider']
AMenu: typeof import('ant-design-vue/es')['Menu']
AMenuItem: typeof import('ant-design-vue/es')['MenuItem']
AModal: typeof import('ant-design-vue/es')['Modal']
ASubMenu: typeof import('ant-design-vue/es')['SubMenu']
ATable: typeof import('ant-design-vue/es')['Table']
ATag: typeof import('ant-design-vue/es')['Tag']
ATextarea: typeof import('ant-design-vue/es')['Textarea']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
Screenfull: typeof import('./components/Screenfull/index.vue')['default']

2
admin/src/components/Screenfull/index.vue

@ -5,7 +5,7 @@
</div>
</template>
<script setup lang='ts'>
import { notification } from 'ant-design-vue';
import { notification } from 'ant-design-vue'
import { onMounted, onUnmounted, ref } from 'vue'
import screenfull from 'screenfull'

10
admin/src/layout/aside.vue

@ -4,14 +4,14 @@
<Sidebar />
</a-layout-sider>
<a-layout style="padding: 0 24px 24px">
<div v-if="layout.dtmVersion && layout.dtmVersion != dashVer" style="color:#f00"> !!! admin version: {{dashVer}} != dtm version: {{layout.dtmVersion}}. </div>
<div v-if="layout.dtmVersion && layout.dtmVersion != dashVer" style="color:#f00"> !!! admin version: {{ dashVer }} != dtm version: {{ layout.dtmVersion }}. </div>
<a-breadcrumb style="margin: 16px 0">
<a-breadcrumb-item>{{ mainNav }}</a-breadcrumb-item>
<a-breadcrumb-item>{{ subNav }}</a-breadcrumb-item>
<a-breadcrumb-item>{{ page }}</a-breadcrumb-item>
</a-breadcrumb>
<a-layout-content
:style="{ background: '#fff', padding: '24px', margin: 0, minHeight: '280px' }"
:style="{ background: '#fff', padding: '24px', margin: 0, minHeight: '280px' }"
>
<Content />
</a-layout-content>
@ -20,14 +20,10 @@
</template>
<script setup lang='ts'>
import Sidebar from './components/sidebar.vue'
import Content from './components/content.vue'
import { useRoute } from 'vue-router'
import { useLayoutStore } from '../store/modules/layout'
import { IMenubarList } from '../type/store/layout'
import { findCurrentMenubar } from '../utils/util'
import { computed, onMounted, ref } from 'vue'
import { getDtmVersion } from '../api/api_dtm'
import { computed, onMounted } from 'vue'
const dashVer = import.meta.env.VITE_ADMIN_VERSION

10
admin/src/layout/components/content.vue

@ -9,8 +9,8 @@
closable
@close="onClose"
>
<template v-slot:description>
<span v-for="ln of errLines">{{ln}} <br/> </span>
<template #description>
<span v-for="(ln, index) of errLines" :key="index">{{ ln }} <br> </span>
</template>
</a-alert>
<component :is="Component" :key="key" />
@ -30,10 +30,10 @@ const route = useRoute()
const key = computed(() => route.path)
const layoutStore = useLayoutStore()
const errLines = computed(() => layoutStore.globalError.split("\n"))
const onClose = async (e: MouseEvent) => {
const errLines = computed(() => layoutStore.globalError.split('\n'))
const onClose = async() => {
await sleep(1000)
layoutStore.setGlobalError("")
layoutStore.setGlobalError('')
}
</script>

22
admin/src/layout/components/header.vue

@ -1,16 +1,16 @@
<template>
<div>
<a-layout-header class="header flex">
<div class="flex items-center logo h-16">
<a-layout-header class="flex header">
<div class="flex items-center h-16 logo">
<svg-icon style="width: 36px; height: 36px; margin-right: 84px;" icon-class="svg-logo" />
<span class="text-gray-400 text-lg">DTM admin {{ version }}</span>
<span class="text-lg text-gray-400">DTM admin {{ version }}</span>
</div>
<a-menu
v-model:selectedKeys="activeMenu"
theme="dark"
mode="horizontal"
:style="{ lineHeight: '64px' }"
@select="onOpenChange"
v-model:selectedKeys="activeMenu"
theme="dark"
mode="horizontal"
:style="{ lineHeight: '64px' }"
@select="onOpenChange"
>
<a-menu-item v-for="v in getMenubar.menuList" :key="v.path">{{ v.meta.title }}</a-menu-item>
</a-menu>
@ -18,13 +18,13 @@
</div>
</template>
<script setup lang='ts'>
import { computed, ref } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useLayoutStore } from '/@/store/modules/layout'
const route = useRoute()
const router = useRouter()
const { getMenubar, getCurrentVersion } = useLayoutStore()
const { getMenubar } = useLayoutStore()
const firstRedirectPath = '/admin'
const version = import.meta.env.VITE_ADMIN_VERSION

4
admin/src/layout/components/sidebar.vue

@ -18,7 +18,7 @@
</template>
<script setup lang='ts'>
import { ref, computed } from 'vue'
import { computed } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useLayoutStore } from '/@/store/modules/layout'
import { IMenubarList } from '/@/type/store/layout'
@ -36,6 +36,7 @@ const activeMenu = computed({
get: () => {
return [route.path]
},
// eslint-disable-next-line @typescript-eslint/no-unused-vars
set: (val) => {
// do nothing, just for eliminate warn
}
@ -46,6 +47,7 @@ const openKeys = computed({
const pos = route.path.lastIndexOf('/')
return [route.path.substring(0, pos)]
},
// eslint-disable-next-line @typescript-eslint/no-unused-vars
set: (val) => {
// do onthing, just for eliminate warn
}

10
admin/src/layout/index.vue

@ -1,12 +1,12 @@
<script setup lang="ts">
import Header from './components/header.vue'
import Header from './components/header.vue'
</script>
<template>
<a-layout>
<a-layout-header class="header">
<Header />
</a-layout-header>
<a-layout>
<a-layout-header class="header">
<Header />
</a-layout-header>
<router-view />
</a-layout>
</template>

18
admin/src/main.ts

@ -15,18 +15,18 @@ app.use(pinia)
app.mount('#app')
window.onunhandledrejection = (ev: PromiseRejectionEvent) => {
showAlert(ev.reason.stack || ev.reason.message)
showAlert(ev.reason.stack || ev.reason.message)
}
window.onerror = err => {
if (typeof err === "string") {
return showAlert(err)
}
showAlert(JSON.stringify(err))
if (typeof err === 'string') {
return showAlert(err)
}
showAlert(JSON.stringify(err))
}
function showAlert(msg: string) {
let layout = useLayoutStore()
if (!layout.globalError) {
layout.setGlobalError(msg)
}
const layout = useLayoutStore()
if (!layout.globalError) {
layout.setGlobalError(msg)
}
}

1
admin/src/permission.ts

@ -4,6 +4,7 @@ import { useLayoutStore } from './store/modules/layout'
configure({ showSpinner: false })
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const defaultRoutePath = '/'
router.beforeEach((to) => {

16
admin/src/store/modules/layout.ts

@ -1,7 +1,7 @@
import { defineStore } from 'pinia';
import { allowRouter } from '/@/router';
import { ILayout, IMenubar, IMenubarList, IStatus } from '/@/type/store/layout';
import { getDtmVersion } from '/@/api/api_dtm';
import { defineStore } from 'pinia'
import { allowRouter } from '/@/router'
import { ILayout, IMenubar, IMenubarList, IStatus } from '/@/type/store/layout'
import { getDtmVersion } from '/@/api/api_dtm'
export const useLayoutStore = defineStore({
id: 'layout',
@ -12,8 +12,8 @@ export const useLayoutStore = defineStore({
status: {
isLoading: false
},
dtmVersion: "",
globalError: "",
dtmVersion: '',
globalError: ''
}),
getters: {
getMenubar(): IMenubar {
@ -21,7 +21,7 @@ export const useLayoutStore = defineStore({
},
getStatus(): IStatus {
return this.status
},
}
},
actions: {
setRoutes(data: Array<IMenubarList>): void {
@ -36,7 +36,7 @@ export const useLayoutStore = defineStore({
async loadDtmVersion(): Promise<void> {
const { data: { version } } = await getDtmVersion()
this.dtmVersion = version
console.log("dtm version: ", this.dtmVersion)
console.log('dtm version: ', this.dtmVersion)
}
}
})

2
admin/src/type/shim.vue.d.ts

@ -1,5 +1,5 @@
declare module '*.vue' {
import { defineComponent } from 'vue';
import { defineComponent } from 'vue'
const Component: ReturnType<typeof defineComponent>
export default Component
}

10
admin/src/utils/util.ts

@ -1,12 +1,12 @@
import { useRoute } from 'vue-router';
import { IMenubarList } from '../type/store/layout';
import { useRoute } from 'vue-router'
import { IMenubarList } from '../type/store/layout'
export const findCurrentMenubar = (menuList: IMenubarList[], root?: boolean) => {
const route = useRoute()
let arr: IMenubarList[] | IMenubarList = []
for (let i = 0; i < menuList.length; i++) {
const v = menuList[i];
const usePath = v.meta.activeMenu || v.redirect || v.path;
const v = menuList[i]
const usePath = v.meta.activeMenu || v.redirect || v.path
const pos = usePath.lastIndexOf('/')
const rootPath = pos == 0 ? usePath : usePath.substring(0, pos)
if (route.path.indexOf(rootPath) !== -1) {
@ -22,6 +22,6 @@ export const findCurrentMenubar = (menuList: IMenubarList[], root?: boolean) =>
return arr
}
export const sleep = async (ms: number) => {
export const sleep = async(ms: number) => {
return new Promise(resolve => setTimeout(resolve, ms))
}

238
admin/src/views/Dashboard/GlobalTransactions/AllTransactions.vue

@ -1,164 +1,170 @@
<template>
<div>
<a-form
layout="inline"
:model="{}"
@finish="searchFinish"
>
<a-form-item>
<a-input v-model:value="gid" placeholder="gid">
</a-input>
</a-form-item>
<a-form-item>
<a-button
type="primary"
html-type="submit">
搜索
</a-button>
</a-form-item>
</a-form>
</div>
<a-divider />
<div>
<a-table :columns="columns" :data-source="dataSource" :loading="loading" :pagination="false">
<template #bodyCell="{column, record}">
<template v-if="column.key === 'status'">
<div>
<a-form
layout="inline"
:model="{}"
@finish="searchFinish"
>
<a-form-item>
<a-input v-model:value="gid" placeholder="gid" />
</a-form-item>
<a-form-item>
<a-button
type="primary"
html-type="submit"
>
搜索
</a-button>
</a-form-item>
</a-form>
</div>
<a-divider />
<div>
<a-table :columns="columns" :data-source="dataSource" :loading="loading" :pagination="false">
<template #bodyCell="{column, record}">
<template v-if="column.key === 'status'">
<span>
<a-tag :key="record.status"
:color="record.status === 'succeed' ? 'green' : 'volcano'">{{ record.status }}</a-tag>
<a-tag
:key="record.status"
:color="record.status === 'succeed' ? 'green' : 'volcano'"
>{{ record.status }}</a-tag>
</span>
</template>
<template v-else-if="column.key === 'action'">
</template>
<template v-else-if="column.key === 'action'">
<span>
<a class="mr-2 font-medium" @click="handleTransactionDetail(record.gid)">Detail</a>
<a-button danger type="link" :disabled="record.status==='failed' || record.status==='succeed'"
@click="handleTransactionStop(record.gid)">ForceStop</a-button>
<!-- <a class="text-red-400 font-medium" @click="handleTransactionStop(record.gid)">ForceStop</a> -->
<a-button
danger
type="link"
:disabled="record.status==='failed' || record.status==='succeed'"
@click="handleTransactionStop(record.gid)"
>ForceStop</a-button>
<!-- <a class="font-medium text-red-400" @click="handleTransactionStop(record.gid)">ForceStop</a> -->
</span>
</template>
</template>
</a-table>
<div class="flex justify-center mt-2 text-lg pager" v-if="canPrev || canNext">
<a-button type="text" :disabled="!canPrev" @click="handlePrevPage">Previous</a-button>
<a-button type="text" :disabled="!canNext" @click="handleNextPage">Next</a-button>
</template>
</template>
</a-table>
<div v-if="canPrev || canNext" class="flex justify-center mt-2 text-lg pager">
<a-button type="text" :disabled="!canPrev" @click="handlePrevPage">Previous</a-button>
<a-button type="text" :disabled="!canNext" @click="handleNextPage">Next</a-button>
</div>
<DialogTransactionDetail ref="transactionDetail" />
</div>
<DialogTransactionDetail ref="transactionDetail"/>
</div>
</template>
<script setup lang="ts">
import {forceStopTransaction, IListAllTransactionsReq, listAllTransactions} from '/@/api/api_dtm'
import {computed, ref} from 'vue-demi'
import {usePagination} from 'vue-request'
import DialogTransactionDetail from './_Components/DialogTransactionDetail.vue';
const gid = ref("")
const searchFinish = function () {
curPage.value = 1;
const params = {
gid: gid.value,
limit: pageSize.value,
}
run(params)
import { forceStopTransaction, IListAllTransactionsReq, listAllTransactions } from '/@/api/api_dtm'
import { computed, ref } from 'vue-demi'
import { usePagination } from 'vue-request'
const gid = ref('')
const searchFinish = function() {
curPage.value = 1
const params = {
gid: gid.value,
limit: pageSize.value
}
run(params)
}
const columns = [
{
title: 'GID',
dataIndex: 'gid',
key: 'gid'
}, {
title: 'TransType',
dataIndex: 'trans_type',
key: 'trans_type'
}, {
title: 'Status',
dataIndex: 'status',
key: 'status'
}, {
title: 'Protocol',
dataIndex: 'protocol',
key: 'protocol'
}, {
title: 'CreateTime',
dataIndex: 'create_time',
key: 'create_time'
}, {
title: 'Action',
key: 'action'
}
{
title: 'GID',
dataIndex: 'gid',
key: 'gid'
}, {
title: 'TransType',
dataIndex: 'trans_type',
key: 'trans_type'
}, {
title: 'Status',
dataIndex: 'status',
key: 'status'
}, {
title: 'Protocol',
dataIndex: 'protocol',
key: 'protocol'
}, {
title: 'CreateTime',
dataIndex: 'create_time',
key: 'create_time'
}, {
title: 'Action',
key: 'action'
}
]
const pages = ref([''])
const curPage = ref(1)
const canPrev = computed(() => {
return curPage.value > 1
return curPage.value > 1
})
const canNext = computed(() => {
return data.value?.data.next_position !== ""
return data.value?.data.next_position !== ''
})
type Data = {
transactions: {
gid: string
trans_type: string
status: string
protocol: string
create_time: string
}[]
next_position: string
transactions: {
gid: string
trans_type: string
status: string
protocol: string
create_time: string
}[]
next_position: string
}
const queryData = (params: IListAllTransactionsReq) => {
return listAllTransactions<Data>(params)
return listAllTransactions<Data>(params)
}
const {data, run, current, loading, pageSize} = usePagination(queryData, {
defaultParams: [
{
limit: 10,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { data, run, current, loading, pageSize } = usePagination(queryData, {
defaultParams: [
{
limit: 10
}
],
pagination: {
pageSizeKey: 'limit'
}
],
pagination: {
pageSizeKey: 'limit'
}
})
const dataSource = computed(() => data.value?.data.transactions || [])
const handlePrevPage = () => {
curPage.value -= 1;
const params = {
limit: pageSize.value,
position: pages.value[curPage.value] as string
}
run(params)
curPage.value -= 1
const params = {
limit: pageSize.value,
position: pages.value[curPage.value] as string
}
run(params)
}
const handleNextPage = () => {
curPage.value += 1;
pages.value[curPage.value] = data.value?.data.next_position as string
curPage.value += 1
pages.value[curPage.value] = data.value?.data.next_position as string
run({
position: data.value?.data.next_position,
limit: pageSize.value,
})
run({
position: data.value?.data.next_position,
limit: pageSize.value
})
}
const transactionDetail = ref<null | { open: (gid: string) => null }>(null)
const handleTransactionDetail = (gid: string) => {
transactionDetail.value?.open(gid)
transactionDetail.value?.open(gid)
}
const handleTransactionStop = async (gid: string) => {
await forceStopTransaction(gid)
run({
position: data.value?.data.next_position,
limit: pageSize.value,
})
const handleTransactionStop = async(gid: string) => {
await forceStopTransaction(gid)
run({
position: data.value?.data.next_position,
limit: pageSize.value
})
}
</script>

15
admin/src/views/Dashboard/GlobalTransactions/_Components/DialogTransactionDetail.vue

@ -2,21 +2,20 @@
<div>
<a-modal v-model:visible="visible" title="Transaction Detail" width="100%" wrap-class-name="full-modal">
<a-table :columns="columns" :data-source="dataSource" :pagination="false">
<template #bodyCell="{column, record}">
</template>
<!-- eslint-disable-next-line vue/no-unused-vars -->
<template #bodyCell="{column, record}" />
</a-table>
<div class="mt-10 relative">
<div class="relative mt-10">
<a-textarea id="qs" v-model:value="textVal" :auto-size="{ minRows: 10, maxRows: 10 }" />
<screenfull class="absolute right-2 top-3 z-50" identity="qs" />
<screenfull class="absolute z-50 right-2 top-3" identity="qs" />
</div>
</a-modal>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { getTransaction } from '/@/api/api_dtm';
import screenfull from '/@/components/Screenfull/index.vue';
import { ref } from 'vue'
import { getTransaction } from '/@/api/api_dtm'
// import VueJsonPretty from 'vue-json-pretty';
// import 'vue-json-pretty/lib/styles.css'
@ -25,7 +24,7 @@ const visible = ref(false)
const textVal = ref('')
const open = async(gid: string) => {
const d = await getTransaction<Data>({gid: gid})
const d = await getTransaction<Data>({ gid: gid })
dataSource.value = d.data.branches
textVal.value = JSON.stringify(d.data, null, 2)
visible.value = true

185
admin/src/views/Dashboard/KVPairs/Topics.vue

@ -1,142 +1,141 @@
<template>
<div>
<a-button type="primary" class="mb-2" @click="handleTopicSubscribe('')">Subscribe</a-button>
<a-table :columns="columns" :data-source="dataSource" :loading="loading" :pagination="false">
<template #bodyCell="{column, record}">
<template v-if="column.key === 'subscribers'">
<span>{{ JSON.parse(record.v).length }}</span>
</template>
<template v-if="column.key === 'action'">
<span>
<a class="mr-2 font-medium" @click="handleTopicSubscribe(record.k)">Subscribe</a>
<a class="mr-2 font-medium" @click="handleTopicDetail(record.k,record.v)">Detail</a>
<a class="text-red-400 font-medium" @click="handleDeleteTopic(record.k)">Delete</a>
</span>
</template>
</template>
</a-table>
<div class="flex justify-center mt-2 text-lg pager" v-if="canPrev || canNext">
<a-button type="text" :disabled="!canPrev" @click="handlePrevPage">Previous</a-button>
<a-button type="text" :disabled="!canNext" @click="handleNextPage">Next</a-button>
<div>
<a-button type="primary" class="mb-2" @click="handleTopicSubscribe('')">Subscribe</a-button>
<a-table :columns="columns" :data-source="dataSource" :loading="loading" :pagination="false">
<template #bodyCell="{column, record}">
<template v-if="column.key === 'subscribers'">
<span>{{ JSON.parse(record.v).length }}</span>
</template>
<template v-if="column.key === 'action'">
<span>
<a class="mr-2 font-medium" @click="handleTopicSubscribe(record.k)">Subscribe</a>
<a class="mr-2 font-medium" @click="handleTopicDetail(record.k,record.v)">Detail</a>
<a class="font-medium text-red-400" @click="handleDeleteTopic(record.k)">Delete</a>
</span>
</template>
</template>
</a-table>
<div v-if="canPrev || canNext" class="flex justify-center mt-2 text-lg pager">
<a-button type="text" :disabled="!canPrev" @click="handlePrevPage">Previous</a-button>
<a-button type="text" :disabled="!canNext" @click="handleNextPage">Next</a-button>
</div>
<DialogTopicDetail ref="topicDetail" @unsubscribed="handleRefreshData" />
<DialogTopicSubscribe ref="topicSubscribe" @subscribed="handleRefreshData" />
</div>
<DialogTopicDetail ref="topicDetail" @unsubscribed="handleRefreshData"/>
<DialogTopicSubscribe ref="topicSubscribe" @subscribed="handleRefreshData"/>
</div>
</template>
<script setup lang="ts">
import {deleteTopic, IListAllKVReq, listKVPairs} from '/@/api/api_dtm'
import {computed, ref} from 'vue-demi'
import {usePagination} from 'vue-request'
import DialogTopicDetail from './_Components/DialogTopicDetail.vue';
import DialogTopicSubscribe from './_Components/DialogTopicSubscribe.vue';
import {message, Modal} from 'ant-design-vue';
import { deleteTopic, IListAllKVReq, listKVPairs } from '/@/api/api_dtm'
import { computed, ref } from 'vue-demi'
import { usePagination } from 'vue-request'
import { message, Modal } from 'ant-design-vue'
const columns = [
{
title: 'Name',
dataIndex: 'k',
key: 'name'
}, {
title: 'Subscribers',
dataIndex: 'v',
key: 'subscribers'
}, {
title: 'Version',
dataIndex: 'version',
key: 'version'
}, {
title: 'Action',
key: 'action'
}
{
title: 'Name',
dataIndex: 'k',
key: 'name'
}, {
title: 'Subscribers',
dataIndex: 'v',
key: 'subscribers'
}, {
title: 'Version',
dataIndex: 'version',
key: 'version'
}, {
title: 'Action',
key: 'action'
}
]
const pages = ref([''])
const curPage = ref(1)
const canPrev = computed(() => {
return curPage.value > 1
return curPage.value > 1
})
const canNext = computed(() => {
return data.value?.data.next_position !== ""
return data.value?.data.next_position !== ''
})
type Data = {
kv: {
k: string
v: string
version: number
}[]
next_position: string
kv: {
k: string
v: string
version: number
}[]
next_position: string
}
const queryData = (params: IListAllKVReq) => {
return listKVPairs<Data>(params)
return listKVPairs<Data>(params)
}
const {data, run, current, loading, pageSize} = usePagination(queryData, {
defaultParams: [
{
cat: "topics",
limit: 10,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { data, run, current, loading, pageSize } = usePagination(queryData, {
defaultParams: [
{
cat: 'topics',
limit: 10
}
],
pagination: {
pageSizeKey: 'limit'
}
],
pagination: {
pageSizeKey: 'limit'
}
})
const dataSource = computed(() => data.value?.data.kv || [])
const handlePrevPage = () => {
curPage.value -= 1;
const params = {
cat: "topics",
limit: pageSize.value,
position: pages.value[curPage.value] as string
}
run(params)
curPage.value -= 1
const params = {
cat: 'topics',
limit: pageSize.value,
position: pages.value[curPage.value] as string
}
run(params)
}
const handleNextPage = () => {
curPage.value += 1;
pages.value[curPage.value] = data.value?.data.next_position as string
run({
cat: "topics",
position: data.value?.data.next_position,
limit: pageSize.value,
})
curPage.value += 1
pages.value[curPage.value] = data.value?.data.next_position as string
run({
cat: 'topics',
position: data.value?.data.next_position,
limit: pageSize.value
})
}
const handleRefreshData = () => {
run({cat: 'topics', limit: pageSize.value})
run({ cat: 'topics', limit: pageSize.value })
}
const handleDeleteTopic = (topic: string) => {
Modal.confirm({
title: 'Delete',
content: 'Do you want delete this topic? ',
okText: 'Yes',
okType: 'danger',
cancelText: 'Cancel',
onOk: async () => {
await deleteTopic(topic)
message.success('Delete topic succeed')
run({cat: 'topics', limit: pageSize.value})
}
})
Modal.confirm({
title: 'Delete',
content: 'Do you want delete this topic? ',
okText: 'Yes',
okType: 'danger',
cancelText: 'Cancel',
onOk: async() => {
await deleteTopic(topic)
message.success('Delete topic succeed')
run({ cat: 'topics', limit: pageSize.value })
}
})
}
const topicDetail = ref<null | { open: (topic: string, subscribers: string) => null }>(null)
const handleTopicDetail = (topic: string, subscribers: string) => {
topicDetail.value?.open(topic, subscribers)
topicDetail.value?.open(topic, subscribers)
}
const topicSubscribe = ref<null | { open: (topic: string) => null }>(null)
const handleTopicSubscribe = (topic: string) => {
topicSubscribe.value?.open(topic)
topicSubscribe.value?.open(topic)
}
</script>

106
admin/src/views/Dashboard/KVPairs/_Components/DialogTopicDetail.vue

@ -1,80 +1,80 @@
<template>
<div>
<a-modal v-model:visible="visible" :title=topicName width="100%" wrap-class-name="full-modal" :footer="null">
<a-table :columns="columns" :data-source="dataSource" :pagination="false">
<template #bodyCell="{column, record}">
<template v-if="column.key === 'action'">
<span>
<a class="text-red-400 font-medium" @click="handleUnsubscribe(record.url)">Unsubscribe</a>
</span>
</template>
</template>
</a-table>
<!-- <div class="mt-10 relative">
<div>
<a-modal v-model:visible="visible" :title="topicName" width="100%" wrap-class-name="full-modal" :footer="null">
<a-table :columns="columns" :data-source="dataSource" :pagination="false">
<template #bodyCell="{column, record}">
<template v-if="column.key === 'action'">
<span>
<a class="text-red-400 font-medium" @click="handleUnsubscribe(record.url)">Unsubscribe</a>
</span>
</template>
</template>
</a-table>
<!-- <div class="mt-10 relative">
<a-textarea id="qs" v-model:value="textVal" :auto-size="{ minRows: 10, maxRows: 10 }" />
<screenfull class="absolute right-2 top-3 z-50" identity="qs" />
</div> -->
</a-modal>
</div>
</a-modal>
</div>
</template>
<script setup lang="ts">
import {ref} from 'vue';
import {unsubscribe} from "/@/api/api_dtm";
import {message, Modal} from "ant-design-vue";
import { ref } from 'vue'
import { unsubscribe } from '/@/api/api_dtm'
import { message, Modal } from 'ant-design-vue'
// import VueJsonPretty from 'vue-json-pretty';
// import 'vue-json-pretty/lib/styles.css'
const dataSource = ref<Subscriber[]>([])
const visible = ref(false)
const topicName = ref<string>("");
const topicName = ref<string>('')
const open = async (topic: string, subscribers: string) => {
dataSource.value = JSON.parse(subscribers)
topicName.value = topic
visible.value = true
const open = async(topic: string, subscribers: string) => {
dataSource.value = JSON.parse(subscribers)
topicName.value = topic
visible.value = true
}
const columns = [
{
title: 'URL',
dataIndex: 'url',
key: 'url'
}, {
title: 'Remark',
dataIndex: 'remark',
key: 'remark'
}, {
title: 'Action',
key: 'action'
}
{
title: 'URL',
dataIndex: 'url',
key: 'url'
}, {
title: 'Remark',
dataIndex: 'remark',
key: 'remark'
}, {
title: 'Action',
key: 'action'
}
]
interface Subscriber {
url: string
remark: string
url: string
remark: string
}
const handleUnsubscribe = async (url: string) => {
Modal.confirm({
title: 'Unsubscribe',
content: 'Do you want unsubscribe this topic?',
okText: 'Yes',
okType: 'danger',
cancelText: 'Cancel',
onOk: async () => {
await unsubscribe({
topic: topicName.value,
url: url
})
message.success('Unsubscribe topic succeed')
location.reload()
}
})
const handleUnsubscribe = async(url: string) => {
Modal.confirm({
title: 'Unsubscribe',
content: 'Do you want unsubscribe this topic?',
okText: 'Yes',
okType: 'danger',
cancelText: 'Cancel',
onOk: async() => {
await unsubscribe({
topic: topicName.value,
url: url
})
message.success('Unsubscribe topic succeed')
location.reload()
}
})
}
defineExpose({
open
open
})
</script>

96
admin/src/views/Dashboard/KVPairs/_Components/DialogTopicSubscribe.vue

@ -1,71 +1,77 @@
<template>
<div>
<a-modal v-model:visible="visible" width="60%" title="Topic Subscribe" :confirm-loading="confirmLoading"
@ok="handleSubscribe">
<a-form v-bind="layout" :mode="form">
<a-form-item label="Topic: ">
<a-input v-model:value="form.topic" placeholder="Please input your topic..."/>
</a-form-item>
<a-form-item label="URL: ">
<a-input v-model:value="form.url" placeholder="Please input your url..."/>
</a-form-item>
<a-form-item label="Remark">
<a-textarea v-model:value="form.remark" :rows="6" placeholder="Please input your remark..."/>
</a-form-item>
</a-form>
</a-modal>
</div>
<div>
<a-modal
v-model:visible="visible"
width="60%"
title="Topic Subscribe"
:confirm-loading="confirmLoading"
@ok="handleSubscribe"
>
<a-form v-bind="layout" :mode="form">
<a-form-item label="Topic: ">
<a-input v-model:value="form.topic" placeholder="Please input your topic..." />
</a-form-item>
<a-form-item label="URL: ">
<a-input v-model:value="form.url" placeholder="Please input your url..." />
</a-form-item>
<a-form-item label="Remark">
<a-textarea v-model:value="form.remark" :rows="6" placeholder="Please input your remark..." />
</a-form-item>
</a-form>
</a-modal>
</div>
</template>
<script setup lang="ts">
import {message} from 'ant-design-vue';
import {reactive, ref} from 'vue';
import {subscribe} from '/@/api/api_dtm'
import { message } from 'ant-design-vue'
import { reactive, ref } from 'vue'
import { subscribe } from '/@/api/api_dtm'
interface formState {
topic: string
url: string
remark: string
topic: string
url: string
remark: string
}
const layout = {
labelCol: {span: 4},
wrapperCol: {span: 16},
labelCol: { span: 4 },
wrapperCol: { span: 16 }
}
const form = reactive<formState>({
topic: '',
url: '',
remark: ''
topic: '',
url: '',
remark: ''
})
const visible = ref(false)
const open = async (topic: string) => {
form.topic = topic
visible.value = true
const open = async(topic: string) => {
form.topic = topic
visible.value = true
}
const emit = defineEmits(['subscribed'])
const confirmLoading = ref<boolean>(false)
const handleSubscribe = async () => {
confirmLoading.value = true
await subscribe<string>(form).then(
() => {
visible.value = false
message.success('Subscribe succeed')
confirmLoading.value = false
emit('subscribed')
}
).catch(() => {
message.error('Failed')
confirmLoading.value = false
return
})
const handleSubscribe = async() => {
confirmLoading.value = true
await subscribe<string>(form).then(
() => {
visible.value = false
message.success('Subscribe succeed')
confirmLoading.value = false
emit('subscribed')
}
)
.catch(() => {
message.error('Failed')
confirmLoading.value = false
return
})
}
defineExpose({
open
open
})
</script>

10
admin/tailwind.config.js

@ -1,7 +1,7 @@
module.exports = {
content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
theme: {
extend: {},
},
plugins: [],
content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
theme: {
extend: {}
},
plugins: []
}

130
admin/vite.config.ts

@ -1,73 +1,73 @@
import { ConfigEnv, UserConfigExport } from "vite";
import path from "path";
import vue from "@vitejs/plugin-vue";
import { createSvgIconsPlugin } from "vite-plugin-svg-icons";
import Components from "unplugin-vue-components/vite";
import { AntDesignVueResolver } from "unplugin-vue-components/resolvers";
import { ViteEjsPlugin } from "vite-plugin-ejs";
import { ConfigEnv, UserConfigExport } from 'vite'
import path from 'path'
import vue from '@vitejs/plugin-vue'
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
import Components from 'unplugin-vue-components/vite'
import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers'
import { ViteEjsPlugin } from 'vite-plugin-ejs'
const setAlias = (alias: [string, string][]) =>
alias.map((v) => {
return { find: v[0], replacement: path.resolve(__dirname, v[1]) };
});
alias.map((v) => {
return { find: v[0], replacement: path.resolve(__dirname, v[1]) }
})
export default ({ mode }: ConfigEnv): UserConfigExport => {
return {
resolve: {
alias: setAlias([["/@", "src"]]),
},
plugins: [
vue(),
createSvgIconsPlugin({
iconDirs: [path.resolve(process.cwd(), "src/icons")],
symbolId: "icon-[dir]-[name]",
}),
Components({
dts: "src/components.d.ts",
resolvers: [AntDesignVueResolver()],
}),
ViteEjsPlugin({
PUBLIC_PATH: mode !== "development" ? "PUBLIC-PATH-VARIABLE" : "",
}),
],
experimental: {
renderBuiltUrl(
filename: string,
{
hostType,
}: {
return {
resolve: {
alias: setAlias([['/@', 'src']])
},
plugins: [
vue(),
createSvgIconsPlugin({
iconDirs: [path.resolve(process.cwd(), 'src/icons')],
symbolId: 'icon-[dir]-[name]'
}),
Components({
dts: 'src/components.d.ts',
resolvers: [AntDesignVueResolver()]
}),
ViteEjsPlugin({
PUBLIC_PATH: mode !== 'development' ? 'PUBLIC-PATH-VARIABLE' : ''
})
],
experimental: {
renderBuiltUrl(
filename: string,
{
hostType
}: {
hostId: string;
hostType: "js" | "css" | "html";
type: "asset" | "public";
}
) {
if (hostType === "js") {
return {
runtime: `window.__assetsPathBuilder(${JSON.stringify(filename)})`,
};
hostType: 'js' | 'css' | 'html';
type: 'asset' | 'public';
}
) {
if (hostType === 'js') {
return {
runtime: `window.__assetsPathBuilder(${JSON.stringify(filename)})`
}
}
return filename;
},
},
server: {
port: 6789,
base: "admin",
proxy: {
"/api": {
target: "http://localhost:36789",
return filename
}
},
},
},
css: {
postcss: {
plugins: [
require("autoprefixer"),
require("tailwindcss"),
require("postcss-simple-vars"),
require("postcss-import"),
],
},
},
};
};
server: {
port: 6789,
base: 'admin',
proxy: {
'/api': {
target: 'http://localhost:36789'
}
}
},
css: {
postcss: {
plugins: [
require('autoprefixer'),
require('tailwindcss'),
require('postcss-simple-vars'),
require('postcss-import')
]
}
}
}
}

Loading…
Cancel
Save