Browse Source

fix lint

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

4
admin/package.json

@ -5,7 +5,9 @@
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "vite build && echo > dist/placeholder", "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": { "dependencies": {
"ant-design-vue": "^3.1.1", "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' { declare module '@vue/runtime-core' {
export interface GlobalComponents { export interface GlobalComponents {
AAlert: typeof import('ant-design-vue/es')['Alert']
ABreadcrumb: typeof import('ant-design-vue/es')['Breadcrumb'] ABreadcrumb: typeof import('ant-design-vue/es')['Breadcrumb']
ABreadcrumbItem: typeof import('ant-design-vue/es')['BreadcrumbItem'] ABreadcrumbItem: typeof import('ant-design-vue/es')['BreadcrumbItem']
AButton: typeof import('ant-design-vue/es')['Button'] 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'] ALayoutSider: typeof import('ant-design-vue/es')['LayoutSider']
AMenu: typeof import('ant-design-vue/es')['Menu'] AMenu: typeof import('ant-design-vue/es')['Menu']
AMenuItem: typeof import('ant-design-vue/es')['MenuItem'] 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'] ATable: typeof import('ant-design-vue/es')['Table']
ATag: typeof import('ant-design-vue/es')['Tag'] ATag: typeof import('ant-design-vue/es')['Tag']
ATextarea: typeof import('ant-design-vue/es')['Textarea']
RouterLink: typeof import('vue-router')['RouterLink'] RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView'] RouterView: typeof import('vue-router')['RouterView']
Screenfull: typeof import('./components/Screenfull/index.vue')['default'] Screenfull: typeof import('./components/Screenfull/index.vue')['default']

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

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

10
admin/src/layout/aside.vue

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

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

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

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

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

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

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

10
admin/src/layout/index.vue

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

18
admin/src/main.ts

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

1
admin/src/permission.ts

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

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

@ -1,7 +1,7 @@
import { defineStore } from 'pinia'; import { defineStore } from 'pinia'
import { allowRouter } from '/@/router'; import { allowRouter } from '/@/router'
import { ILayout, IMenubar, IMenubarList, IStatus } from '/@/type/store/layout'; import { ILayout, IMenubar, IMenubarList, IStatus } from '/@/type/store/layout'
import { getDtmVersion } from '/@/api/api_dtm'; import { getDtmVersion } from '/@/api/api_dtm'
export const useLayoutStore = defineStore({ export const useLayoutStore = defineStore({
id: 'layout', id: 'layout',
@ -12,8 +12,8 @@ export const useLayoutStore = defineStore({
status: { status: {
isLoading: false isLoading: false
}, },
dtmVersion: "", dtmVersion: '',
globalError: "", globalError: ''
}), }),
getters: { getters: {
getMenubar(): IMenubar { getMenubar(): IMenubar {
@ -21,7 +21,7 @@ export const useLayoutStore = defineStore({
}, },
getStatus(): IStatus { getStatus(): IStatus {
return this.status return this.status
}, }
}, },
actions: { actions: {
setRoutes(data: Array<IMenubarList>): void { setRoutes(data: Array<IMenubarList>): void {
@ -36,7 +36,7 @@ export const useLayoutStore = defineStore({
async loadDtmVersion(): Promise<void> { async loadDtmVersion(): Promise<void> {
const { data: { version } } = await getDtmVersion() const { data: { version } } = await getDtmVersion()
this.dtmVersion = version 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' { declare module '*.vue' {
import { defineComponent } from 'vue'; import { defineComponent } from 'vue'
const Component: ReturnType<typeof defineComponent> const Component: ReturnType<typeof defineComponent>
export default Component export default Component
} }

10
admin/src/utils/util.ts

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

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

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

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

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

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

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

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

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

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

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

10
admin/tailwind.config.js

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

130
admin/vite.config.ts

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

Loading…
Cancel
Save