This commit is contained in:
ayowang 2025-07-21 19:47:45 +08:00
parent b0986a7454
commit c5685cd01d
131 changed files with 7093 additions and 38252 deletions

7
src/.idea/.idea.ZhonTai/.idea/vcs.xml generated Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
</component>
</project>

6597
zhontai.ui.admin.vue3/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,312 +0,0 @@
/* eslint-disable */
/* tslint:disable */
// @ts-nocheck
/*
* ---------------------------------------------------------------
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
* ## ##
* ## AUTHOR: acacode ##
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
* ---------------------------------------------------------------
*/
import { AxiosResponse } from 'axios'
import {
ApiAddInput,
ApiSetEnableLogInput,
ApiSetEnableParamsInput,
ApiSetEnableResultInput,
ApiSyncInput,
ApiUpdateInput,
PageInputApiGetPageInput,
ResultOutputApiGetOutput,
ResultOutputInt64,
ResultOutputListApiGetListOutput,
ResultOutputListProjectConfig,
ResultOutputPageOutputApiEntity,
} from './data-contracts'
import { ContentType, HttpClient, RequestParams } from './http-client'
export class ApiApi<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
/**
* No description
*
* @tags api
* @name Get
* @summary
* @request GET:/api/admin/api/get
* @secure
*/
get = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<ResultOutputApiGetOutput, any>({
path: `/api/admin/api/get`,
method: 'GET',
query: query,
secure: true,
format: 'json',
...params,
})
/**
* No description
*
* @tags api
* @name GetList
* @summary
* @request GET:/api/admin/api/get-list
* @secure
*/
getList = (
query?: {
key?: string
},
params: RequestParams = {}
) =>
this.request<ResultOutputListApiGetListOutput, any>({
path: `/api/admin/api/get-list`,
method: 'GET',
query: query,
secure: true,
format: 'json',
...params,
})
/**
* No description
*
* @tags api
* @name GetPage
* @summary
* @request POST:/api/admin/api/get-page
* @secure
*/
getPage = (data: PageInputApiGetPageInput, params: RequestParams = {}) =>
this.request<ResultOutputPageOutputApiEntity, any>({
path: `/api/admin/api/get-page`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* No description
*
* @tags api
* @name Add
* @summary
* @request POST:/api/admin/api/add
* @secure
*/
add = (data: ApiAddInput, params: RequestParams = {}) =>
this.request<ResultOutputInt64, any>({
path: `/api/admin/api/add`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* No description
*
* @tags api
* @name Update
* @summary
* @request PUT:/api/admin/api/update
* @secure
*/
update = (data: ApiUpdateInput, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/api/update`,
method: 'PUT',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags api
* @name SetEnableLog
* @summary
* @request POST:/api/admin/api/set-enable-log
* @secure
*/
setEnableLog = (data: ApiSetEnableLogInput, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/api/set-enable-log`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags api
* @name SetEnableParams
* @summary
* @request POST:/api/admin/api/set-enable-params
* @secure
*/
setEnableParams = (data: ApiSetEnableParamsInput, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/api/set-enable-params`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags api
* @name SetEnableResult
* @summary
* @request POST:/api/admin/api/set-enable-result
* @secure
*/
setEnableResult = (data: ApiSetEnableResultInput, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/api/set-enable-result`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags api
* @name Delete
* @summary
* @request DELETE:/api/admin/api/delete
* @secure
*/
delete = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<AxiosResponse, any>({
path: `/api/admin/api/delete`,
method: 'DELETE',
query: query,
secure: true,
...params,
})
/**
* No description
*
* @tags api
* @name BatchDelete
* @summary
* @request PUT:/api/admin/api/batch-delete
* @secure
*/
batchDelete = (data: number[], params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/api/batch-delete`,
method: 'PUT',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags api
* @name SoftDelete
* @summary
* @request DELETE:/api/admin/api/soft-delete
* @secure
*/
softDelete = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<AxiosResponse, any>({
path: `/api/admin/api/soft-delete`,
method: 'DELETE',
query: query,
secure: true,
...params,
})
/**
* No description
*
* @tags api
* @name BatchSoftDelete
* @summary
* @request PUT:/api/admin/api/batch-soft-delete
* @secure
*/
batchSoftDelete = (data: number[], params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/api/batch-soft-delete`,
method: 'PUT',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags api
* @name Sync
* @summary
* @request POST:/api/admin/api/sync
* @secure
*/
sync = (data: ApiSyncInput, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/api/sync`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags api
* @name GetProjects
* @summary
* @request GET:/api/admin/api/get-projects
* @secure
*/
getProjects = (
query?: {
/** @default "/swagger" */
suffix?: string
},
params: RequestParams = {}
) =>
this.request<ResultOutputListProjectConfig, any>({
path: `/api/admin/api/get-projects`,
method: 'GET',
query: query,
secure: true,
format: 'json',
...params,
})
}

View File

@ -1,229 +0,0 @@
/* eslint-disable */
/* tslint:disable */
// @ts-nocheck
/*
* ---------------------------------------------------------------
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
* ## ##
* ## AUTHOR: acacode ##
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
* ---------------------------------------------------------------
*/
import { AxiosResponse } from 'axios'
import {
BoardAddInput,
BoardGetOutput,
BoardGetPageInput,
BoardGetPageOutput,
BoardUpdateInput,
PageInputBoardGetPageInput,
ResultOutputBoardGetOutput,
ResultOutputInt64,
ResultOutputPageOutputBoardGetPageOutput,
BoardGetBoardUserListOutput,
BoardAddBoardUserListInput,
BoardRemoveBoardUserInput,
BoardGetBoardListByUserIdOutput,
} from './data-contracts'
import { ContentType, HttpClient, RequestParams } from './http-client'
export class BoardApi<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
/**
* No description
*
* @tags board
* @name Get
* @summary
* @request GET:/api/admin/board/get
* @secure
*/
get = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<ResultOutputBoardGetOutput, any>({
path: `/api/admin/board/get`,
method: 'GET',
query: query,
secure: true,
format: 'json',
...params,
})
/**
* No description
*
* @tags board
* @name GetPage
* @summary
* @request POST:/api/admin/board/get-page
* @secure
*/
getPage = (data: PageInputBoardGetPageInput, params: RequestParams = {}) =>
this.request<ResultOutputPageOutputBoardGetPageOutput, any>({
path: `/api/admin/board/get-page`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* No description
*
* @tags board
* @name Add
* @summary
* @request POST:/api/admin/board/add
* @secure
*/
add = (data: BoardAddInput, params: RequestParams = {}) =>
this.request<ResultOutputInt64, any>({
path: `/api/admin/board/add`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* No description
*
* @tags board
* @name Update
* @summary
* @request PUT:/api/admin/board/update
* @secure
*/
update = (data: BoardUpdateInput, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/board/update`,
method: 'PUT',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags board
* @name SoftDelete
* @summary
* @request DELETE:/api/admin/board/soft-delete
* @secure
*/
softDelete = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<AxiosResponse, any>({
path: `/api/admin/board/soft-delete`,
method: 'DELETE',
query: query,
secure: true,
...params,
})
// GetBoardListByUserIdAsync
/**
* No description
*
* @tags board
* @name GetBoardUserList
* @summary
* @request GET:/api/admin/board/get-board-user-list
* @secure
*/
getBoardUserList = (
query?: {
/** @format int64 */
BoardId?: number
/** 姓名 */
Name?: string | null
},
params: RequestParams = {}
) =>
this.request<{ success?: boolean; data?: BoardGetBoardUserListOutput[] }, any>({
path: `/api/admin/board/get-board-user-list`,
method: 'GET',
query: query,
secure: true,
format: 'json',
...params,
})
/**
* No description
*
* @tags board
* @name AddBoardUser
* @summary
* @request POST:/api/admin/board/add-board-user
* @secure
*/
addBoardUser = (data: BoardAddBoardUserListInput, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/board/add-board-user`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags board
* @name RemoveBoardUser
* @summary
* @request POST:/api/admin/board/remove-board-user
* @secure
*/
removeBoardUser = (data: BoardRemoveBoardUserInput, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/board/remove-board-user`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags board
* @name GetBoardListByUserId
* @summary
* @request GET:/api/admin/board/get-board-list-by-user-id
* @secure
*/
getBoardListByUserId = (
query?: {
/** 看板名称 */
name?: string | null
},
params: RequestParams = {}
) =>
this.request<{ success?: boolean; data?: BoardGetBoardListByUserIdOutput[] }, any>({
path: `/api/admin/board/get-board-list-by-user-id`,
method: 'GET',
query: query,
secure: true,
format: 'json',
...params,
})
}

View File

@ -1,58 +0,0 @@
/* eslint-disable */
/* tslint:disable */
// @ts-nocheck
/*
* ---------------------------------------------------------------
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
* ## ##
* ## AUTHOR: acacode ##
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
* ---------------------------------------------------------------
*/
import { AxiosResponse } from 'axios'
import { ResultOutputListObject } from './data-contracts'
import { HttpClient, RequestParams } from './http-client'
export class CacheApi<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
/**
* No description
*
* @tags cache
* @name GetList
* @summary
* @request GET:/api/admin/cache/get-list
* @secure
*/
getList = (params: RequestParams = {}) =>
this.request<ResultOutputListObject, any>({
path: `/api/admin/cache/get-list`,
method: 'GET',
secure: true,
format: 'json',
...params,
})
/**
* No description
*
* @tags cache
* @name Clear
* @summary
* @request DELETE:/api/admin/cache/clear
* @secure
*/
clear = (
query?: {
/** 缓存键 */
cacheKey?: string
},
params: RequestParams = {}
) =>
this.request<AxiosResponse, any>({
path: `/api/admin/cache/clear`,
method: 'DELETE',
query: query,
secure: true,
...params,
})
}

View File

@ -1,30 +0,0 @@
import { HttpClient, ContentType, RequestParams } from './http-client'
import { CustomNavigationInput } from './data-contracts'
export class CustomNavigationApi<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
/**
*
* @request POST:/api/user/custom-navigation
*/
save = (data: CustomNavigationInput, params: RequestParams = {}) =>
this.request<any, any>({
path: '/api/admin/user-custom-navigation/save',
method: 'POST',
body: data,
type: ContentType.Json,
format: 'json',
...params,
})
/**
*
* @request GET:/api/user/custom-navigation
*/
get = (params: RequestParams = {}) =>
this.request<any, any>({
path: '/api/admin/user-custom-navigation/get',
method: 'GET',
format: 'json',
...params,
})
}

View File

@ -1,316 +0,0 @@
/* eslint-disable */
/* tslint:disable */
// @ts-nocheck
/*
* ---------------------------------------------------------------
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
* ## ##
* ## AUTHOR: acacode ##
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
* ---------------------------------------------------------------
*/
import { AxiosResponse } from 'axios'
import {
DictAddInput,
DictUpdateInput,
ExportInput,
PageInputDictGetPageInput,
ResultOutputDictGetOutput,
ResultOutputDictionaryStringListDictGetListOutput,
ResultOutputImportOutput,
ResultOutputInt64,
ResultOutputPageOutputDictGetPageOutput,
} from './data-contracts'
import { ContentType, HttpClient, RequestParams } from './http-client'
export class DictApi<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
/**
* No description
*
* @tags dict
* @name Get
* @summary
* @request GET:/api/admin/dict/get
* @secure
*/
get = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<ResultOutputDictGetOutput, any>({
path: `/api/admin/dict/get`,
method: 'GET',
query: query,
secure: true,
format: 'json',
...params,
})
/**
* No description
*
* @tags dict
* @name GetPage
* @summary
* @request POST:/api/admin/dict/get-page
* @secure
*/
getPage = (data: PageInputDictGetPageInput, params: RequestParams = {}) =>
this.request<ResultOutputPageOutputDictGetPageOutput, any>({
path: `/api/admin/dict/get-page`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* No description
*
* @tags dict
* @name GetList
* @summary
* @request POST:/api/admin/dict/get-list
* @secure
*/
getList = (data: string[], params: RequestParams = {}) =>
this.request<ResultOutputDictionaryStringListDictGetListOutput, any>({
path: `/api/admin/dict/get-list`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* No description
*
* @tags dict
* @name GetListByNames
* @summary
* @request POST:/api/admin/dict/get-list-by-names
* @secure
*/
getListByNames = (data: string[], params: RequestParams = {}) =>
this.request<ResultOutputDictionaryStringListDictGetListOutput, any>({
path: `/api/admin/dict/get-list-by-names`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* No description
*
* @tags dict
* @name DownloadTemplate
* @summary
* @request POST:/api/admin/dict/download-template
* @secure
*/
downloadTemplate = (params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/dict/download-template`,
method: 'POST',
secure: true,
...params,
})
/**
* No description
*
* @tags dict
* @name DownloadErrorMark
* @summary
* @request POST:/api/admin/dict/download-error-mark
* @secure
*/
downloadErrorMark = (
query?: {
fileId?: string
fileName?: string
},
params: RequestParams = {}
) =>
this.request<AxiosResponse, any>({
path: `/api/admin/dict/download-error-mark`,
method: 'POST',
query: query,
secure: true,
...params,
})
/**
* No description
*
* @tags dict
* @name ExportData
* @summary
* @request POST:/api/admin/dict/export-data
* @secure
*/
exportData = (data: ExportInput, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/dict/export-data`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags dict
* @name ImportData
* @summary
* @request POST:/api/admin/dict/import-data
* @secure
*/
importData = (
data: {
/** @format binary */
file: File
},
query?: {
/** @format int32 */
duplicateAction?: number
fileId?: string
},
params: RequestParams = {}
) =>
this.request<ResultOutputImportOutput, any>({
path: `/api/admin/dict/import-data`,
method: 'POST',
query: query,
body: data,
secure: true,
type: ContentType.FormData,
format: 'json',
...params,
})
/**
* No description
*
* @tags dict
* @name Add
* @summary
* @request POST:/api/admin/dict/add
* @secure
*/
add = (data: DictAddInput, params: RequestParams = {}) =>
this.request<ResultOutputInt64, any>({
path: `/api/admin/dict/add`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* No description
*
* @tags dict
* @name Update
* @summary
* @request PUT:/api/admin/dict/update
* @secure
*/
update = (data: DictUpdateInput, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/dict/update`,
method: 'PUT',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags dict
* @name Delete
* @summary
* @request DELETE:/api/admin/dict/delete
* @secure
*/
delete = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<AxiosResponse, any>({
path: `/api/admin/dict/delete`,
method: 'DELETE',
query: query,
secure: true,
...params,
})
/**
* No description
*
* @tags dict
* @name BatchDelete
* @summary
* @request PUT:/api/admin/dict/batch-delete
* @secure
*/
batchDelete = (data: number[], params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/dict/batch-delete`,
method: 'PUT',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags dict
* @name SoftDelete
* @summary
* @request DELETE:/api/admin/dict/soft-delete
* @secure
*/
softDelete = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<AxiosResponse, any>({
path: `/api/admin/dict/soft-delete`,
method: 'DELETE',
query: query,
secure: true,
...params,
})
/**
* No description
*
* @tags dict
* @name BatchSoftDelete
* @summary
* @request PUT:/api/admin/dict/batch-soft-delete
* @secure
*/
batchSoftDelete = (data: number[], params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/dict/batch-soft-delete`,
method: 'PUT',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
}

View File

@ -1,187 +0,0 @@
/* eslint-disable */
/* tslint:disable */
// @ts-nocheck
/*
* ---------------------------------------------------------------
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
* ## ##
* ## AUTHOR: acacode ##
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
* ---------------------------------------------------------------
*/
import { AxiosResponse } from 'axios'
import {
DictTypeAddInput,
DictTypeUpdateInput,
PageInputDictTypeGetPageInput,
ResultOutputDictTypeGetOutput,
ResultOutputInt64,
ResultOutputPageOutputDictTypeGetPageOutput,
} from './data-contracts'
import { ContentType, HttpClient, RequestParams } from './http-client'
export class DictTypeApi<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
/**
* No description
*
* @tags dict-type
* @name Get
* @summary
* @request GET:/api/admin/dict-type/get
* @secure
*/
get = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<ResultOutputDictTypeGetOutput, any>({
path: `/api/admin/dict-type/get`,
method: 'GET',
query: query,
secure: true,
format: 'json',
...params,
})
/**
* No description
*
* @tags dict-type
* @name GetPage
* @summary
* @request POST:/api/admin/dict-type/get-page
* @secure
*/
getPage = (data: PageInputDictTypeGetPageInput, params: RequestParams = {}) =>
this.request<ResultOutputPageOutputDictTypeGetPageOutput, any>({
path: `/api/admin/dict-type/get-page`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* No description
*
* @tags dict-type
* @name Add
* @summary
* @request POST:/api/admin/dict-type/add
* @secure
*/
add = (data: DictTypeAddInput, params: RequestParams = {}) =>
this.request<ResultOutputInt64, any>({
path: `/api/admin/dict-type/add`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* No description
*
* @tags dict-type
* @name Update
* @summary
* @request PUT:/api/admin/dict-type/update
* @secure
*/
update = (data: DictTypeUpdateInput, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/dict-type/update`,
method: 'PUT',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags dict-type
* @name Delete
* @summary
* @request DELETE:/api/admin/dict-type/delete
* @secure
*/
delete = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<AxiosResponse, any>({
path: `/api/admin/dict-type/delete`,
method: 'DELETE',
query: query,
secure: true,
...params,
})
/**
* No description
*
* @tags dict-type
* @name BatchDelete
* @summary
* @request PUT:/api/admin/dict-type/batch-delete
* @secure
*/
batchDelete = (data: number[], params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/dict-type/batch-delete`,
method: 'PUT',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags dict-type
* @name SoftDelete
* @summary
* @request DELETE:/api/admin/dict-type/soft-delete
* @secure
*/
softDelete = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<AxiosResponse, any>({
path: `/api/admin/dict-type/soft-delete`,
method: 'DELETE',
query: query,
secure: true,
...params,
})
/**
* No description
*
* @tags dict-type
* @name BatchSoftDelete
* @summary
* @request PUT:/api/admin/dict-type/batch-soft-delete
* @secure
*/
batchSoftDelete = (data: number[], params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/dict-type/batch-soft-delete`,
method: 'PUT',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
}

View File

@ -1,387 +0,0 @@
/* eslint-disable */
/* tslint:disable */
// @ts-nocheck
/*
* ---------------------------------------------------------------
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
* ## ##
* ## AUTHOR: acacode ##
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
* ---------------------------------------------------------------
*/
import { AxiosResponse } from 'axios'
import {
DocAddGroupInput,
DocAddImageInput,
DocAddMenuInput,
DocUpdateContentInput,
DocUpdateGroupInput,
DocUpdateMenuInput,
ResultOutputDocGetContentOutput,
ResultOutputDocGetGroupOutput,
ResultOutputDocGetMenuOutput,
ResultOutputIEnumerableObject,
ResultOutputInt64,
ResultOutputListDocListOutput,
ResultOutputListString,
ResultOutputString,
} from './data-contracts'
import { ContentType, HttpClient, RequestParams } from './http-client'
export class DocApi<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
/**
* No description
*
* @tags doc
* @name GetGroup
* @summary
* @request GET:/api/admin/doc/get-group
* @secure
*/
getGroup = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<ResultOutputDocGetGroupOutput, any>({
path: `/api/admin/doc/get-group`,
method: 'GET',
query: query,
secure: true,
format: 'json',
...params,
})
/**
* No description
*
* @tags doc
* @name GetMenu
* @summary
* @request GET:/api/admin/doc/get-menu
* @secure
*/
getMenu = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<ResultOutputDocGetMenuOutput, any>({
path: `/api/admin/doc/get-menu`,
method: 'GET',
query: query,
secure: true,
format: 'json',
...params,
})
/**
* No description
*
* @tags doc
* @name GetContent
* @summary
* @request GET:/api/admin/doc/get-content
* @secure
*/
getContent = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<ResultOutputDocGetContentOutput, any>({
path: `/api/admin/doc/get-content`,
method: 'GET',
query: query,
secure: true,
format: 'json',
...params,
})
/**
* No description
*
* @tags doc
* @name GetList
* @summary
* @request GET:/api/admin/doc/get-list
* @secure
*/
getList = (
query?: {
key?: string
/** @format date-time */
start?: string
/** @format date-time */
end?: string
},
params: RequestParams = {}
) =>
this.request<ResultOutputListDocListOutput, any>({
path: `/api/admin/doc/get-list`,
method: 'GET',
query: query,
secure: true,
format: 'json',
...params,
})
/**
* No description
*
* @tags doc
* @name GetImageList
* @summary
* @request GET:/api/admin/doc/get-image-list
* @secure
*/
getImageList = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<ResultOutputListString, any>({
path: `/api/admin/doc/get-image-list`,
method: 'GET',
query: query,
secure: true,
format: 'json',
...params,
})
/**
* No description
*
* @tags doc
* @name AddGroup
* @summary
* @request POST:/api/admin/doc/add-group
* @secure
*/
addGroup = (data: DocAddGroupInput, params: RequestParams = {}) =>
this.request<ResultOutputInt64, any>({
path: `/api/admin/doc/add-group`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* No description
*
* @tags doc
* @name AddMenu
* @summary
* @request POST:/api/admin/doc/add-menu
* @secure
*/
addMenu = (data: DocAddMenuInput, params: RequestParams = {}) =>
this.request<ResultOutputInt64, any>({
path: `/api/admin/doc/add-menu`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* No description
*
* @tags doc
* @name AddImage
* @summary
* @request POST:/api/admin/doc/add-image
* @secure
*/
addImage = (data: DocAddImageInput, params: RequestParams = {}) =>
this.request<ResultOutputInt64, any>({
path: `/api/admin/doc/add-image`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* No description
*
* @tags doc
* @name UpdateGroup
* @summary
* @request PUT:/api/admin/doc/update-group
* @secure
*/
updateGroup = (data: DocUpdateGroupInput, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/doc/update-group`,
method: 'PUT',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags doc
* @name UpdateMenu
* @summary
* @request PUT:/api/admin/doc/update-menu
* @secure
*/
updateMenu = (data: DocUpdateMenuInput, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/doc/update-menu`,
method: 'PUT',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags doc
* @name UpdateContent
* @summary
* @request PUT:/api/admin/doc/update-content
* @secure
*/
updateContent = (data: DocUpdateContentInput, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/doc/update-content`,
method: 'PUT',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags doc
* @name Delete
* @summary
* @request DELETE:/api/admin/doc/delete
* @secure
*/
delete = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<AxiosResponse, any>({
path: `/api/admin/doc/delete`,
method: 'DELETE',
query: query,
secure: true,
...params,
})
/**
* No description
*
* @tags doc
* @name DeleteImage
* @summary
* @request DELETE:/api/admin/doc/delete-image
* @secure
*/
deleteImage = (
query?: {
/** @format int64 */
documentId?: number
url?: string
},
params: RequestParams = {}
) =>
this.request<AxiosResponse, any>({
path: `/api/admin/doc/delete-image`,
method: 'DELETE',
query: query,
secure: true,
...params,
})
/**
* No description
*
* @tags doc
* @name SoftDelete
* @summary
* @request DELETE:/api/admin/doc/soft-delete
* @secure
*/
softDelete = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<AxiosResponse, any>({
path: `/api/admin/doc/soft-delete`,
method: 'DELETE',
query: query,
secure: true,
...params,
})
/**
* No description
*
* @tags doc
* @name GetPlainList
* @summary
* @request GET:/api/admin/doc/get-plain-list
* @secure
*/
getPlainList = (params: RequestParams = {}) =>
this.request<ResultOutputIEnumerableObject, any>({
path: `/api/admin/doc/get-plain-list`,
method: 'GET',
secure: true,
format: 'json',
...params,
})
/**
* No description
*
* @tags doc
* @name UploadImage
* @summary
* @request POST:/api/admin/doc/upload-image
* @secure
*/
uploadImage = (
data: {
/**
*
* @format binary
*/
File?: File
/**
*
* @format int64
*/
Id?: number
},
params: RequestParams = {}
) =>
this.request<ResultOutputString, any>({
path: `/api/admin/doc/upload-image`,
method: 'POST',
body: data,
secure: true,
type: ContentType.FormData,
format: 'json',
...params,
})
}

View File

@ -1,260 +0,0 @@
import { AxiosResponse } from 'axios'
import { ContentType, HttpClient, RequestParams } from './http-client'
// 提交反馈的输入参数
export interface FeedbackSubmitInput {
title: string
feedbackType: any
feedbackTeam: string
feedbackPersonName: string
feedbackPersonEmail: string
relatedPersons: string
content: string
attachmentPaths: string
}
// 获取反馈分页的输入参数
export interface PageInputFeedbackGetPageInput {
currentPage: number
pageSize: number
filter: {
title?: string
feedbackType?: any
feedbackTeam?: string
feedbackPersonName?: string
status?: any
createdTimeStart?: string
createdTimeEnd?: string
}
dynamicFilter?: any
}
// 反馈列表输出项
export interface FeedbackGetPageOutput {
id: number
title: string
feedbackType: any
feedbackTypeName: string
feedbackTeam: string
feedbackPersonName: string
feedbackPersonEmail: string
status: any
statusName: string
processorName: string
processTime: string
contentSummary: string
createdTime: string
}
// 反馈详情输出
export interface FeedbackGetOutput {
id: number
title: string
feedbackType: any
feedbackTypeName: string
feedbackTeam: string
feedbackPersonName: string
feedbackPersonEmail: string
status: any
statusName: string
processorName: string
processTime: string
contentSummary: string
createdTime: string
content: string
relatedPersons: string
attachmentPaths: string
processRemark: string
createdUserId: number
createdUserName: string
modifiedTime: string
}
// 分页输出结果
export interface PageOutputFeedbackGetPageOutput {
total: number
list: FeedbackGetPageOutput[]
}
// 结果输出包装
export interface ResultOutputPageOutputFeedbackGetPageOutput {
success: boolean
code: string
msg: string
data: PageOutputFeedbackGetPageOutput
}
export interface ResultOutputFeedbackGetOutput {
success: boolean
code: string
msg: string
data: FeedbackGetOutput
}
export interface ResultOutputInt64 {
success: boolean
code: string
msg: string
data: number
}
// 标记处理状态的输入参数
export interface FeedbackMarkInput {
feedbackId: number
processRemark: string
}
// 团队负责人信息
export interface TeamProcessorOutput {
id: number
teamName: string
site: string
processorId: number
processorName: string
processorEmail: string
displayName: string
}
export interface ResultOutputTeamProcessorOutput {
success: boolean
code: string
msg: string
data: TeamProcessorOutput[]
}
export class FeedbackApi<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
/**
* @name submitFeedback
* @summary
* @request POST:/api/admin/feedback/submit-feedback
* @secure
*/
submitFeedback = (data: FeedbackSubmitInput, params: RequestParams = {}) =>
this.request<ResultOutputInt64, any>({
path: `/api/admin/feedback/submit-feedback`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* @name getPage
* @summary
* @request POST:/api/admin/feedback/get-page
* @secure
*/
getPage = (data: PageInputFeedbackGetPageInput, params: RequestParams = {}) =>
this.request<ResultOutputPageOutputFeedbackGetPageOutput, any>({
path: `/api/admin/feedback/get-page`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* @name getPendingPage
* @summary
* @request POST:/api/admin/feedback/get-pending-page
* @secure
*/
getPendingPage = (data: PageInputFeedbackGetPageInput, params: RequestParams = {}) =>
this.request<ResultOutputPageOutputFeedbackGetPageOutput, any>({
path: `/api/admin/feedback/get-pending-page`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* @name get
* @summary
* @request GET:/api/admin/feedback/get
* @secure
*/
get = (
query?: {
id?: number
},
params: RequestParams = {}
) =>
this.request<ResultOutputFeedbackGetOutput, any>({
path: `/api/admin/feedback/get`,
method: 'GET',
query: query,
secure: true,
format: 'json',
...params,
})
/**
* @name markAsProcessing
* @summary
* @request POST:/api/admin/feedback/mark-as-processing
* @secure
*/
markAsProcessing = (data: FeedbackMarkInput, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/feedback/mark-as-processing`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* @name markAsCompleted
* @summary
* @request POST:/api/admin/feedback/mark-as-completed
* @secure
*/
markAsCompleted = (data: FeedbackMarkInput, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/feedback/mark-as-completed`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* @name markAsRejected
* @summary
* @request POST:/api/admin/feedback/mark-as-rejected
* @secure
*/
markAsRejected = (data: FeedbackMarkInput, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/feedback/mark-as-rejected`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* @name getTeamProcessors
* @summary
* @request GET:/api/admin/feedback/get-team-processors
* @secure
*/
getTeamProcessors = (params: RequestParams = {}) =>
this.request<ResultOutputTeamProcessorOutput, any>({
path: `/api/admin/feedback/get-team-processors`,
method: 'GET',
secure: true,
format: 'json',
...params,
})
}

View File

@ -1,140 +0,0 @@
/* eslint-disable */
/* tslint:disable */
// @ts-nocheck
/*
* ---------------------------------------------------------------
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
* ## ##
* ## AUTHOR: acacode ##
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
* ---------------------------------------------------------------
*/
import { AxiosResponse } from 'axios'
import {
FileDeleteInput,
PageInputFileGetPageInput,
ResultOutputFileEntity,
ResultOutputListFileEntity,
ResultOutputPageOutputFileGetPageOutput,
} from './data-contracts'
import { ContentType, HttpClient, RequestParams } from './http-client'
export class FileApi<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
/**
* No description
*
* @tags file
* @name GetPage
* @summary
* @request POST:/api/admin/file/get-page
* @secure
*/
getPage = (data: PageInputFileGetPageInput, params: RequestParams = {}) =>
this.request<ResultOutputPageOutputFileGetPageOutput, any>({
path: `/api/admin/file/get-page`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* No description
*
* @tags file
* @name Delete
* @summary
* @request POST:/api/admin/file/delete
* @secure
*/
delete = (data: FileDeleteInput, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/file/delete`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags file
* @name UploadFile
* @summary
* @request POST:/api/admin/file/upload-file
* @secure
*/
uploadFile = (
data: {
/**
*
* @format binary
*/
file: File
},
query?: {
/**
*
* @default ""
*/
fileDirectory?: string
/**
*
* @default true
*/
fileReName?: boolean
},
params: RequestParams = {}
) =>
this.request<ResultOutputFileEntity, any>({
path: `/api/admin/file/upload-file`,
method: 'POST',
query: query,
body: data,
secure: true,
type: ContentType.FormData,
format: 'json',
...params,
})
/**
* No description
*
* @tags file
* @name UploadFiles
* @summary
* @request POST:/api/admin/file/upload-files
* @secure
*/
uploadFiles = (
data: {
/** 文件列表 */
files: File[]
},
query?: {
/**
*
* @default ""
*/
fileDirectory?: string
/**
*
* @default true
*/
fileReName?: boolean
},
params: RequestParams = {}
) =>
this.request<ResultOutputListFileEntity, any>({
path: `/api/admin/file/upload-files`,
method: 'POST',
query: query,
body: data,
secure: true,
type: ContentType.FormData,
format: 'json',
...params,
})
}

View File

@ -1,55 +0,0 @@
/* eslint-disable */
/* tslint:disable */
// @ts-nocheck
/*
* ---------------------------------------------------------------
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
* ## ##
* ## AUTHOR: acacode ##
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
* ---------------------------------------------------------------
*/
import { LoginLogAddInput, PageInputLoginLogGetPageInput, ResultOutputInt64, ResultOutputPageOutputLoginLogGetPageOutput } from './data-contracts'
import { ContentType, HttpClient, RequestParams } from './http-client'
export class LoginLogApi<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
/**
* No description
*
* @tags login-log
* @name GetPage
* @summary
* @request POST:/api/admin/login-log/get-page
* @secure
*/
getPage = (data: PageInputLoginLogGetPageInput, params: RequestParams = {}) =>
this.request<ResultOutputPageOutputLoginLogGetPageOutput, any>({
path: `/api/admin/login-log/get-page`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* No description
*
* @tags login-log
* @name Add
* @summary
* @request POST:/api/admin/login-log/add
* @secure
*/
add = (data: LoginLogAddInput, params: RequestParams = {}) =>
this.request<ResultOutputInt64, any>({
path: `/api/admin/login-log/add`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
}

View File

@ -1,254 +0,0 @@
/* eslint-disable */
/* tslint:disable */
// @ts-nocheck
/*
* ---------------------------------------------------------------
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
* ## ##
* ## AUTHOR: acacode ##
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
* ---------------------------------------------------------------
*/
import { AxiosResponse } from 'axios'
import {
MsgAddInput,
MsgAddMsgUserListInput,
MsgUpdateInput,
PageInputMsgGetPageInput,
ResultOutputInt64,
ResultOutputListMsgGetMsgUserListOutput,
ResultOutputMsgGetOutput,
ResultOutputPageOutputMsgGetPageOutput,
} from './data-contracts'
import { ContentType, HttpClient, RequestParams } from './http-client'
export class MsgApi<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
/**
* No description
*
* @tags msg
* @name Get
* @summary
* @request GET:/api/admin/msg/get
* @secure
*/
get = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<ResultOutputMsgGetOutput, any>({
path: `/api/admin/msg/get`,
method: 'GET',
query: query,
secure: true,
format: 'json',
...params,
})
/**
* No description
*
* @tags msg
* @name GetPage
* @summary
* @request POST:/api/admin/msg/get-page
* @secure
*/
getPage = (data: PageInputMsgGetPageInput, params: RequestParams = {}) =>
this.request<ResultOutputPageOutputMsgGetPageOutput, any>({
path: `/api/admin/msg/get-page`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* No description
*
* @tags msg
* @name GetMsgUserList
* @summary
* @request GET:/api/admin/msg/get-msg-user-list
* @secure
*/
getMsgUserList = (
query?: {
/**
* Id
* @format int64
*/
MsgId?: number
/** 姓名 */
Name?: string
},
params: RequestParams = {}
) =>
this.request<ResultOutputListMsgGetMsgUserListOutput, any>({
path: `/api/admin/msg/get-msg-user-list`,
method: 'GET',
query: query,
secure: true,
format: 'json',
...params,
})
/**
* No description
*
* @tags msg
* @name AddMsgUser
* @summary
* @request POST:/api/admin/msg/add-msg-user
* @secure
*/
addMsgUser = (data: MsgAddMsgUserListInput, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/msg/add-msg-user`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags msg
* @name RemoveMsgUser
* @summary
* @request POST:/api/admin/msg/remove-msg-user
* @secure
*/
removeMsgUser = (data: MsgAddMsgUserListInput, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/msg/remove-msg-user`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags msg
* @name Add
* @summary
* @request POST:/api/admin/msg/add
* @secure
*/
add = (data: MsgAddInput, params: RequestParams = {}) =>
this.request<ResultOutputInt64, any>({
path: `/api/admin/msg/add`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* No description
*
* @tags msg
* @name Update
* @summary
* @request PUT:/api/admin/msg/update
* @secure
*/
update = (data: MsgUpdateInput, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/msg/update`,
method: 'PUT',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags msg
* @name Delete
* @summary
* @request DELETE:/api/admin/msg/delete
* @secure
*/
delete = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<AxiosResponse, any>({
path: `/api/admin/msg/delete`,
method: 'DELETE',
query: query,
secure: true,
...params,
})
/**
* No description
*
* @tags msg
* @name BatchDelete
* @summary
* @request PUT:/api/admin/msg/batch-delete
* @secure
*/
batchDelete = (data: number[], params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/msg/batch-delete`,
method: 'PUT',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags msg
* @name SoftDelete
* @summary
* @request DELETE:/api/admin/msg/soft-delete
* @secure
*/
softDelete = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<AxiosResponse, any>({
path: `/api/admin/msg/soft-delete`,
method: 'DELETE',
query: query,
secure: true,
...params,
})
/**
* No description
*
* @tags msg
* @name BatchSoftDelete
* @summary
* @request PUT:/api/admin/msg/batch-soft-delete
* @secure
*/
batchSoftDelete = (data: number[], params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/msg/batch-soft-delete`,
method: 'PUT',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
}

View File

@ -1,191 +0,0 @@
/* eslint-disable */
/* tslint:disable */
// @ts-nocheck
/*
* ---------------------------------------------------------------
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
* ## ##
* ## AUTHOR: acacode ##
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
* ---------------------------------------------------------------
*/
import { AxiosResponse } from 'axios'
import {
MsgTypeAddInput,
MsgTypeUpdateInput,
ResultOutputInt64,
ResultOutputListMsgTypeGetListOutput,
ResultOutputMsgTypeGetOutput,
} from './data-contracts'
import { ContentType, HttpClient, RequestParams } from './http-client'
export class MsgTypeApi<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
/**
* No description
*
* @tags msg-type
* @name Get
* @summary
* @request GET:/api/admin/msg-type/get
* @secure
*/
get = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<ResultOutputMsgTypeGetOutput, any>({
path: `/api/admin/msg-type/get`,
method: 'GET',
query: query,
secure: true,
format: 'json',
...params,
})
/**
* No description
*
* @tags msg-type
* @name GetList
* @summary
* @request GET:/api/admin/msg-type/get-list
* @secure
*/
getList = (
query?: {
/** 名称 */
Name?: string
},
params: RequestParams = {}
) =>
this.request<ResultOutputListMsgTypeGetListOutput, any>({
path: `/api/admin/msg-type/get-list`,
method: 'GET',
query: query,
secure: true,
format: 'json',
...params,
})
/**
* No description
*
* @tags msg-type
* @name Add
* @summary
* @request POST:/api/admin/msg-type/add
* @secure
*/
add = (data: MsgTypeAddInput, params: RequestParams = {}) =>
this.request<ResultOutputInt64, any>({
path: `/api/admin/msg-type/add`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* No description
*
* @tags msg-type
* @name Update
* @summary
* @request PUT:/api/admin/msg-type/update
* @secure
*/
update = (data: MsgTypeUpdateInput, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/msg-type/update`,
method: 'PUT',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags msg-type
* @name Delete
* @summary
* @request DELETE:/api/admin/msg-type/delete
* @secure
*/
delete = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<AxiosResponse, any>({
path: `/api/admin/msg-type/delete`,
method: 'DELETE',
query: query,
secure: true,
...params,
})
/**
* No description
*
* @tags msg-type
* @name BatchDelete
* @summary
* @request PUT:/api/admin/msg-type/batch-delete
* @secure
*/
batchDelete = (data: number[], params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/msg-type/batch-delete`,
method: 'PUT',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags msg-type
* @name SoftDelete
* @summary
* @request DELETE:/api/admin/msg-type/soft-delete
* @secure
*/
softDelete = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<AxiosResponse, any>({
path: `/api/admin/msg-type/soft-delete`,
method: 'DELETE',
query: query,
secure: true,
...params,
})
/**
* No description
*
* @tags msg-type
* @name BatchSoftDelete
* @summary
* @request PUT:/api/admin/msg-type/batch-soft-delete
* @secure
*/
batchSoftDelete = (data: number[], params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/msg-type/batch-soft-delete`,
method: 'PUT',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
}

View File

@ -1,148 +0,0 @@
/* eslint-disable */
/* tslint:disable */
// @ts-nocheck
/*
* ---------------------------------------------------------------
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
* ## ##
* ## AUTHOR: acacode ##
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
* ---------------------------------------------------------------
*/
import { AxiosResponse } from 'axios'
import {
OnlineAddInput,
OnlineUpdateInput,
PageInputOnlineGetPageInput,
ResultOutputOnlineGetOutput,
ResultOutputPageOutputOnlineGetPageOutput,
} from './data-contracts'
import { ContentType, HttpClient, RequestParams } from './http-client'
export class OnlineApi<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
/**
* No description
*
* @tags online
* @name Get
* @summary 线
* @request GET:/api/admin/online/get
* @secure
*/
get = (
query?: {
id?: number
},
params: RequestParams = {}
) =>
this.request<ResultOutputOnlineGetOutput, any>({
path: `/api/admin/online/get`,
method: 'GET',
query: query,
secure: true,
format: 'json',
...params,
})
/**
* No description
*
* @tags online
* @name GetPage
* @summary
* @request POST:/api/admin/online/get-page
* @secure
*/
getPage = (data: PageInputOnlineGetPageInput, params: RequestParams = {}) =>
this.request<ResultOutputPageOutputOnlineGetPageOutput, any>({
path: `/api/admin/online/get-page`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* No description
*
* @tags online
* @name GetList
* @summary
* @request POST:/api/admin/online/get-list
* @secure
*/
getList = (data: string[], params: RequestParams = {}) =>
this.request<ResultOutputListOutputOnlineGetListOutput, any>({
path: `/api/admin/online/get-list`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* No description
*
* @tags online
* @name Add
* @summary
* @request POST:/api/admin/online/add
* @secure
*/
add = (data: OnlineAddInput, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/online/add`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags online
* @name Update
* @summary
* @request PUT:/api/admin/online/update
* @secure
*/
update = (data: OnlineUpdateInput, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/online/update`,
method: 'PUT',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags online
* @name Delete
* @summary
* @request DELETE:/api/admin/online/delete
* @secure
*/
delete = (
query: {
id: number
},
params: RequestParams = {}
) =>
this.request<AxiosResponse, any>({
path: `/api/admin/online/delete`,
method: 'DELETE',
query: query,
secure: true,
...params,
})
}

View File

@ -1,60 +0,0 @@
/* eslint-disable */
/* tslint:disable */
// @ts-nocheck
/*
* ---------------------------------------------------------------
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
* ## ##
* ## AUTHOR: acacode ##
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
* ---------------------------------------------------------------
*/
import {
OperationLogAddInput,
PageInputOperationLogGetPageInput,
ResultOutputInt64,
ResultOutputPageOutputOperationLogGetPageOutput,
} from './data-contracts'
import { ContentType, HttpClient, RequestParams } from './http-client'
export class OperationLogApi<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
/**
* No description
*
* @tags operation-log
* @name GetPage
* @summary
* @request POST:/api/admin/operation-log/get-page
* @secure
*/
getPage = (data: PageInputOperationLogGetPageInput, params: RequestParams = {}) =>
this.request<ResultOutputPageOutputOperationLogGetPageOutput, any>({
path: `/api/admin/operation-log/get-page`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* No description
*
* @tags operation-log
* @name Add
* @summary
* @request POST:/api/admin/operation-log/add
* @secure
*/
add = (data: OperationLogAddInput, params: RequestParams = {}) =>
this.request<ResultOutputInt64, any>({
path: `/api/admin/operation-log/add`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
}

View File

@ -1,172 +0,0 @@
/* eslint-disable */
/* tslint:disable */
// @ts-nocheck
/*
* ---------------------------------------------------------------
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
* ## ##
* ## AUTHOR: acacode ##
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
* ---------------------------------------------------------------
*/
import { AxiosResponse } from 'axios'
import {
OrgAddInput,
OrgUpdateInput,
ResultOutputInt64,
ResultOutputListOrgGetListOutput,
ResultOutputListOrgGetSimpleListWithPathOutput,
ResultOutputOrgGetOutput,
} from './data-contracts'
import { ContentType, HttpClient, RequestParams } from './http-client'
export class OrgApi<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
/**
* No description
*
* @tags org
* @name Get
* @summary
* @request GET:/api/admin/org/get
* @secure
*/
get = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<ResultOutputOrgGetOutput, any>({
path: `/api/admin/org/get`,
method: 'GET',
query: query,
secure: true,
format: 'json',
...params,
})
/**
* No description
*
* @tags org
* @name GetList
* @summary
* @request GET:/api/admin/org/get-list
* @secure
*/
getList = (
query?: {
key?: string
},
params: RequestParams = {}
) =>
this.request<ResultOutputListOrgGetListOutput, any>({
path: `/api/admin/org/get-list`,
method: 'GET',
query: query,
secure: true,
format: 'json',
...params,
})
/**
* No description
*
* @tags org
* @name GetSimpleListWithPath
* @summary
* @request GET:/api/admin/org/get-simple-list-with-path
* @secure
*/
getSimpleListWithPath = (params: RequestParams = {}) =>
this.request<ResultOutputListOrgGetSimpleListWithPathOutput, any>({
path: `/api/admin/org/get-simple-list-with-path`,
method: 'GET',
secure: true,
format: 'json',
...params,
})
/**
* No description
*
* @tags org
* @name Add
* @summary
* @request POST:/api/admin/org/add
* @secure
*/
add = (data: OrgAddInput, params: RequestParams = {}) =>
this.request<ResultOutputInt64, any>({
path: `/api/admin/org/add`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* No description
*
* @tags org
* @name Update
* @summary
* @request PUT:/api/admin/org/update
* @secure
*/
update = (data: OrgUpdateInput, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/org/update`,
method: 'PUT',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags org
* @name Delete
* @summary
* @request DELETE:/api/admin/org/delete
* @secure
*/
delete = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<AxiosResponse, any>({
path: `/api/admin/org/delete`,
method: 'DELETE',
query: query,
secure: true,
...params,
})
/**
* No description
*
* @tags org
* @name SoftDelete
* @summary
* @request DELETE:/api/admin/org/soft-delete
* @secure
*/
softDelete = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<AxiosResponse, any>({
path: `/api/admin/org/soft-delete`,
method: 'DELETE',
query: query,
secure: true,
...params,
})
}

View File

@ -1,347 +0,0 @@
/* eslint-disable */
/* tslint:disable */
// @ts-nocheck
/*
* ---------------------------------------------------------------
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
* ## ##
* ## AUTHOR: acacode ##
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
* ---------------------------------------------------------------
*/
import { AxiosResponse } from 'axios'
import {
PageInputPkgGetPageInput,
PageInputPkgGetPkgTenantListInput,
PkgAddInput,
PkgAddPkgTenantListInput,
PkgSetPkgPermissionsInput,
PkgUpdateInput,
ResultOutputInt64,
ResultOutputListInt64,
ResultOutputListPkgGetListOutput,
ResultOutputListPkgGetPkgTenantListOutput,
ResultOutputPageOutputPkgGetPageOutput,
ResultOutputPageOutputPkgGetPkgTenantListOutput,
ResultOutputPkgGetOutput,
} from './data-contracts'
import { ContentType, HttpClient, RequestParams } from './http-client'
export class PkgApi<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
/**
* No description
*
* @tags pkg
* @name Get
* @summary
* @request GET:/api/admin/pkg/get
* @secure
*/
get = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<ResultOutputPkgGetOutput, any>({
path: `/api/admin/pkg/get`,
method: 'GET',
query: query,
secure: true,
format: 'json',
...params,
})
/**
* No description
*
* @tags pkg
* @name GetList
* @summary
* @request GET:/api/admin/pkg/get-list
* @secure
*/
getList = (
query?: {
/** 名称 */
Name?: string
},
params: RequestParams = {}
) =>
this.request<ResultOutputListPkgGetListOutput, any>({
path: `/api/admin/pkg/get-list`,
method: 'GET',
query: query,
secure: true,
format: 'json',
...params,
})
/**
* No description
*
* @tags pkg
* @name GetPage
* @summary
* @request POST:/api/admin/pkg/get-page
* @secure
*/
getPage = (data: PageInputPkgGetPageInput, params: RequestParams = {}) =>
this.request<ResultOutputPageOutputPkgGetPageOutput, any>({
path: `/api/admin/pkg/get-page`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* No description
*
* @tags pkg
* @name GetPkgTenantList
* @summary
* @request GET:/api/admin/pkg/get-pkg-tenant-list
* @secure
*/
getPkgTenantList = (
query?: {
/** 租户名 */
TenantName?: string
/**
* Id
* @format int64
*/
PkgId?: number
},
params: RequestParams = {}
) =>
this.request<ResultOutputListPkgGetPkgTenantListOutput, any>({
path: `/api/admin/pkg/get-pkg-tenant-list`,
method: 'GET',
query: query,
secure: true,
format: 'json',
...params,
})
/**
* No description
*
* @tags pkg
* @name GetPkgTenantPage
* @summary
* @request POST:/api/admin/pkg/get-pkg-tenant-page
* @secure
*/
getPkgTenantPage = (data: PageInputPkgGetPkgTenantListInput, params: RequestParams = {}) =>
this.request<ResultOutputPageOutputPkgGetPkgTenantListOutput, any>({
path: `/api/admin/pkg/get-pkg-tenant-page`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* No description
*
* @tags pkg
* @name GetPkgPermissionList
* @summary
* @request GET:/api/admin/pkg/get-pkg-permission-list
* @secure
*/
getPkgPermissionList = (
query?: {
/**
*
* @format int64
*/
pkgId?: number
},
params: RequestParams = {}
) =>
this.request<ResultOutputListInt64, any>({
path: `/api/admin/pkg/get-pkg-permission-list`,
method: 'GET',
query: query,
secure: true,
format: 'json',
...params,
})
/**
* No description
*
* @tags pkg
* @name SetPkgPermissions
* @summary
* @request POST:/api/admin/pkg/set-pkg-permissions
* @secure
*/
setPkgPermissions = (data: PkgSetPkgPermissionsInput, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/pkg/set-pkg-permissions`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags pkg
* @name AddPkgTenant
* @summary
* @request POST:/api/admin/pkg/add-pkg-tenant
* @secure
*/
addPkgTenant = (data: PkgAddPkgTenantListInput, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/pkg/add-pkg-tenant`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags pkg
* @name RemovePkgTenant
* @summary
* @request POST:/api/admin/pkg/remove-pkg-tenant
* @secure
*/
removePkgTenant = (data: PkgAddPkgTenantListInput, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/pkg/remove-pkg-tenant`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags pkg
* @name Add
* @summary
* @request POST:/api/admin/pkg/add
* @secure
*/
add = (data: PkgAddInput, params: RequestParams = {}) =>
this.request<ResultOutputInt64, any>({
path: `/api/admin/pkg/add`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* No description
*
* @tags pkg
* @name Update
* @summary
* @request PUT:/api/admin/pkg/update
* @secure
*/
update = (data: PkgUpdateInput, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/pkg/update`,
method: 'PUT',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags pkg
* @name Delete
* @summary
* @request DELETE:/api/admin/pkg/delete
* @secure
*/
delete = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<AxiosResponse, any>({
path: `/api/admin/pkg/delete`,
method: 'DELETE',
query: query,
secure: true,
...params,
})
/**
* No description
*
* @tags pkg
* @name BatchDelete
* @summary
* @request PUT:/api/admin/pkg/batch-delete
* @secure
*/
batchDelete = (data: number[], params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/pkg/batch-delete`,
method: 'PUT',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags pkg
* @name SoftDelete
* @summary
* @request DELETE:/api/admin/pkg/soft-delete
* @secure
*/
softDelete = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<AxiosResponse, any>({
path: `/api/admin/pkg/soft-delete`,
method: 'DELETE',
query: query,
secure: true,
...params,
})
/**
* No description
*
* @tags pkg
* @name BatchSoftDelete
* @summary
* @request PUT:/api/admin/pkg/batch-soft-delete
* @secure
*/
batchSoftDelete = (data: number[], params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/pkg/batch-soft-delete`,
method: 'PUT',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
}

View File

@ -1,250 +0,0 @@
/* eslint-disable */
/* tslint:disable */
// @ts-nocheck
/*
* ---------------------------------------------------------------
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
* ## ##
* ## AUTHOR: acacode ##
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
* ---------------------------------------------------------------
*/
import { AxiosResponse } from 'axios'
import {
PageInputPrintTemplateGetPageInput,
PrintTemplateAddInput,
PrintTemplateSetEnableInput,
PrintTemplateUpdateInput,
PrintTemplateUpdateTemplateInput,
ResultOutputInt64,
ResultOutputPageOutputPrintTemplateGetPageOutput,
ResultOutputPrintTemplateGetOutput,
ResultOutputPrintTemplateGetUpdateTemplateOutput,
} from './data-contracts'
import { ContentType, HttpClient, RequestParams } from './http-client'
export class PrintTemplateApi<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
/**
* No description
*
* @tags print-template
* @name Get
* @summary
* @request GET:/api/admin/print-template/get
* @secure
*/
get = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<ResultOutputPrintTemplateGetOutput, any>({
path: `/api/admin/print-template/get`,
method: 'GET',
query: query,
secure: true,
format: 'json',
...params,
})
/**
* No description
*
* @tags print-template
* @name GetUpdateTemplate
* @summary
* @request GET:/api/admin/print-template/get-update-template
* @secure
*/
getUpdateTemplate = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<ResultOutputPrintTemplateGetUpdateTemplateOutput, any>({
path: `/api/admin/print-template/get-update-template`,
method: 'GET',
query: query,
secure: true,
format: 'json',
...params,
})
/**
* No description
*
* @tags print-template
* @name GetPage
* @summary
* @request POST:/api/admin/print-template/get-page
* @secure
*/
getPage = (data: PageInputPrintTemplateGetPageInput, params: RequestParams = {}) =>
this.request<ResultOutputPageOutputPrintTemplateGetPageOutput, any>({
path: `/api/admin/print-template/get-page`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* No description
*
* @tags print-template
* @name Add
* @summary
* @request POST:/api/admin/print-template/add
* @secure
*/
add = (data: PrintTemplateAddInput, params: RequestParams = {}) =>
this.request<ResultOutputInt64, any>({
path: `/api/admin/print-template/add`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* No description
*
* @tags print-template
* @name Update
* @summary
* @request PUT:/api/admin/print-template/update
* @secure
*/
update = (data: PrintTemplateUpdateInput, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/print-template/update`,
method: 'PUT',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags print-template
* @name UpdateTemplate
* @summary
* @request PUT:/api/admin/print-template/update-template
* @secure
*/
updateTemplate = (data: PrintTemplateUpdateTemplateInput, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/print-template/update-template`,
method: 'PUT',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags print-template
* @name SetEnable
* @summary
* @request POST:/api/admin/print-template/set-enable
* @secure
*/
setEnable = (data: PrintTemplateSetEnableInput, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/print-template/set-enable`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags print-template
* @name Delete
* @summary
* @request DELETE:/api/admin/print-template/delete
* @secure
*/
delete = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<AxiosResponse, any>({
path: `/api/admin/print-template/delete`,
method: 'DELETE',
query: query,
secure: true,
...params,
})
/**
* No description
*
* @tags print-template
* @name BatchDelete
* @summary
* @request PUT:/api/admin/print-template/batch-delete
* @secure
*/
batchDelete = (data: number[], params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/print-template/batch-delete`,
method: 'PUT',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags print-template
* @name SoftDelete
* @summary
* @request DELETE:/api/admin/print-template/soft-delete
* @secure
*/
softDelete = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<AxiosResponse, any>({
path: `/api/admin/print-template/soft-delete`,
method: 'DELETE',
query: query,
secure: true,
...params,
})
/**
* No description
*
* @tags print-template
* @name BatchSoftDelete
* @summary
* @request PUT:/api/admin/print-template/batch-soft-delete
* @secure
*/
batchSoftDelete = (data: number[], params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/print-template/batch-soft-delete`,
method: 'PUT',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
}

View File

@ -1,229 +0,0 @@
/* eslint-disable */
/* tslint:disable */
// @ts-nocheck
/*
* ---------------------------------------------------------------
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
* ## ##
* ## AUTHOR: acacode ##
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
* ---------------------------------------------------------------
*/
import { AxiosResponse } from 'axios'
import {
PageInputRegionGetPageInput,
RegionAddInput,
RegionGetListInput,
RegionLevel,
RegionSetEnableInput,
RegionSetHotInput,
RegionUpdateInput,
ResultOutputInt64,
ResultOutputListRegionGetChildListOutput,
ResultOutputPageOutputRegionGetPageOutput,
ResultOutputRegionGetOutput,
} from './data-contracts'
import { ContentType, HttpClient, RequestParams } from './http-client'
export class RegionApi<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
/**
* No description
*
* @tags region
* @name Get
* @summary
* @request GET:/api/admin/region/get
* @secure
*/
get = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<ResultOutputRegionGetOutput, any>({
path: `/api/admin/region/get`,
method: 'GET',
query: query,
secure: true,
format: 'json',
...params,
})
/**
* No description
*
* @tags region
* @name GetChildList
* @summary
* @request POST:/api/admin/region/get-child-list
* @secure
*/
getChildList = (data: RegionGetListInput, params: RequestParams = {}) =>
this.request<ResultOutputListRegionGetChildListOutput, any>({
path: `/api/admin/region/get-child-list`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* No description
*
* @tags region
* @name GetPage
* @summary
* @request POST:/api/admin/region/get-page
* @secure
*/
getPage = (data: PageInputRegionGetPageInput, params: RequestParams = {}) =>
this.request<ResultOutputPageOutputRegionGetPageOutput, any>({
path: `/api/admin/region/get-page`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* No description
*
* @tags region
* @name Add
* @summary
* @request POST:/api/admin/region/add
* @secure
*/
add = (data: RegionAddInput, params: RequestParams = {}) =>
this.request<ResultOutputInt64, any>({
path: `/api/admin/region/add`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* No description
*
* @tags region
* @name Update
* @summary
* @request PUT:/api/admin/region/update
* @secure
*/
update = (data: RegionUpdateInput, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/region/update`,
method: 'PUT',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags region
* @name Delete
* @summary
* @request DELETE:/api/admin/region/delete
* @secure
*/
delete = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<AxiosResponse, any>({
path: `/api/admin/region/delete`,
method: 'DELETE',
query: query,
secure: true,
...params,
})
/**
* No description
*
* @tags region
* @name SoftDelete
* @summary
* @request DELETE:/api/admin/region/soft-delete
* @secure
*/
softDelete = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<AxiosResponse, any>({
path: `/api/admin/region/soft-delete`,
method: 'DELETE',
query: query,
secure: true,
...params,
})
/**
* No description
*
* @tags region
* @name SetEnable
* @summary
* @request POST:/api/admin/region/set-enable
* @secure
*/
setEnable = (data: RegionSetEnableInput, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/region/set-enable`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags region
* @name SetHot
* @summary
* @request POST:/api/admin/region/set-hot
* @secure
*/
setHot = (data: RegionSetHotInput, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/region/set-hot`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags region
* @name SyncData
* @summary
* @request POST:/api/admin/region/sync-data
* @secure
*/
syncData = (data: RegionLevel, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/region/sync-data`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
}

View File

@ -1,154 +0,0 @@
/* eslint-disable */
/* tslint:disable */
// @ts-nocheck
/*
* ---------------------------------------------------------------
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
* ## ##
* ## AUTHOR: acacode ##
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
* ---------------------------------------------------------------
*/
import { AxiosResponse } from 'axios'
import {
RemoteAddInput,
RemoteUpdateInput,
PageInputRemoteGetPageInput,
ResultOutputRemoteGetOutput,
ResultOutputInt64,
ResultOutputPageOutputRemoteGetPageOutput,
RemoteConnectInput,
ResultOutputRemoteConnectOutput
} from './data-contracts'
import { ContentType, HttpClient, RequestParams } from './http-client'
export class RemoteApi<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
/**
* No description
*
* @tags remote
* @name Get
* @summary Remote
* @request GET:/api/admin/remote/get
* @secure
*/
get = (
query?: {
id?: number
},
params: RequestParams = {}
) =>
this.request<ResultOutputRemoteGetOutput, any>({
path: `/api/admin/remote/get`,
method: 'GET',
query: query,
secure: true,
format: 'json',
...params,
})
/**
* No description
*
* @tags remote
* @name GetPage
* @summary
* @request POST:/api/admin/remote/get-page
* @secure
*/
getPage = (data: PageInputRemoteGetPageInput, params: RequestParams = {}) =>
this.request<ResultOutputPageOutputRemoteGetPageOutput, any>({
path: `/api/admin/remote/get-page`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* No description
*
* @tags remote
* @name Add
* @summary
* @request POST:/api/admin/remote/add
* @secure
*/
add = (data: RemoteAddInput, params: RequestParams = {}) =>
this.request<ResultOutputRemoteGetOutput, any>({
path: `/api/admin/remote/add`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* No description
*
* @tags remote
* @name Update
* @summary
* @request PUT:/api/admin/remote/update
* @secure
*/
update = (data: RemoteUpdateInput, params: RequestParams = {}) =>
this.request<ResultOutputRemoteGetOutput, any>({
path: `/api/admin/remote/update`,
method: 'PUT',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* No description
*
* @tags remote
* @name Delete
* @summary
* @request DELETE:/api/admin/remote/delete
* @secure
*/
delete = (
query?: {
id?: number
},
params: RequestParams = {}
) =>
this.request<ResultOutputRemoteGetOutput, any>({
path: `/api/admin/remote/delete`,
method: 'DELETE',
query: query,
secure: true,
format: 'json',
...params,
})
/**
* No description
*
* @tags remote
* @name Connect
* @summary
* @request POST:/api/admin/remote/connect
* @secure
*/
connect = (data: RemoteConnectInput, params: RequestParams = {}) =>
this.request<ResultOutputRemoteConnectOutput, any>({
path: `/api/admin/remote/remote-connect`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
}

View File

@ -1,298 +0,0 @@
/* eslint-disable */
/* tslint:disable */
// @ts-nocheck
/*
* ---------------------------------------------------------------
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
* ## ##
* ## AUTHOR: acacode ##
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
* ---------------------------------------------------------------
*/
import { AxiosResponse } from 'axios'
import {
PageInputRoleGetPageInput,
ResultOutputInt64,
ResultOutputListRoleGetListOutput,
ResultOutputListRoleGetRoleUserListOutput,
ResultOutputPageOutputRoleGetPageOutput,
ResultOutputRoleGetOutput,
RoleAddInput,
RoleAddRoleUserListInput,
RoleSetDataScopeInput,
RoleUpdateInput,
} from './data-contracts'
import { ContentType, HttpClient, RequestParams } from './http-client'
export class RoleApi<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
/**
* No description
*
* @tags role
* @name Get
* @summary
* @request GET:/api/admin/role/get
* @secure
*/
get = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<ResultOutputRoleGetOutput, any>({
path: `/api/admin/role/get`,
method: 'GET',
query: query,
secure: true,
format: 'json',
...params,
})
/**
* No description
*
* @tags role
* @name GetList
* @summary
* @request GET:/api/admin/role/get-list
* @secure
*/
getList = (
query?: {
/** 名称 */
Name?: string
},
params: RequestParams = {}
) =>
this.request<ResultOutputListRoleGetListOutput, any>({
path: `/api/admin/role/get-list`,
method: 'GET',
query: query,
secure: true,
format: 'json',
...params,
})
/**
* No description
*
* @tags role
* @name GetPage
* @summary
* @request POST:/api/admin/role/get-page
* @secure
*/
getPage = (data: PageInputRoleGetPageInput, params: RequestParams = {}) =>
this.request<ResultOutputPageOutputRoleGetPageOutput, any>({
path: `/api/admin/role/get-page`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* No description
*
* @tags role
* @name GetRoleUserList
* @summary
* @request GET:/api/admin/role/get-role-user-list
* @secure
*/
getRoleUserList = (
query?: {
/** 姓名 */
Name?: string
/**
* Id
* @format int64
*/
RoleId?: number
},
params: RequestParams = {}
) =>
this.request<ResultOutputListRoleGetRoleUserListOutput, any>({
path: `/api/admin/role/get-role-user-list`,
method: 'GET',
query: query,
secure: true,
format: 'json',
...params,
})
/**
* No description
*
* @tags role
* @name AddRoleUser
* @summary
* @request POST:/api/admin/role/add-role-user
* @secure
*/
addRoleUser = (data: RoleAddRoleUserListInput, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/role/add-role-user`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags role
* @name RemoveRoleUser
* @summary
* @request POST:/api/admin/role/remove-role-user
* @secure
*/
removeRoleUser = (data: RoleAddRoleUserListInput, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/role/remove-role-user`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags role
* @name Add
* @summary
* @request POST:/api/admin/role/add
* @secure
*/
add = (data: RoleAddInput, params: RequestParams = {}) =>
this.request<ResultOutputInt64, any>({
path: `/api/admin/role/add`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* No description
*
* @tags role
* @name Update
* @summary
* @request PUT:/api/admin/role/update
* @secure
*/
update = (data: RoleUpdateInput, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/role/update`,
method: 'PUT',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags role
* @name Delete
* @summary
* @request DELETE:/api/admin/role/delete
* @secure
*/
delete = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<AxiosResponse, any>({
path: `/api/admin/role/delete`,
method: 'DELETE',
query: query,
secure: true,
...params,
})
/**
* No description
*
* @tags role
* @name BatchDelete
* @summary
* @request PUT:/api/admin/role/batch-delete
* @secure
*/
batchDelete = (data: number[], params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/role/batch-delete`,
method: 'PUT',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags role
* @name SoftDelete
* @summary
* @request DELETE:/api/admin/role/soft-delete
* @secure
*/
softDelete = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<AxiosResponse, any>({
path: `/api/admin/role/soft-delete`,
method: 'DELETE',
query: query,
secure: true,
...params,
})
/**
* No description
*
* @tags role
* @name BatchSoftDelete
* @summary
* @request PUT:/api/admin/role/batch-soft-delete
* @secure
*/
batchSoftDelete = (data: number[], params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/role/batch-soft-delete`,
method: 'PUT',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags role
* @name SetDataScope
* @summary
* @request POST:/api/admin/role/set-data-scope
* @secure
*/
setDataScope = (data: RoleSetDataScopeInput, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/role/set-data-scope`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
}

View File

@ -1,113 +0,0 @@
/* eslint-disable */
/* tslint:disable */
// @ts-nocheck
/*
* ---------------------------------------------------------------
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
* ## ##
* ## AUTHOR: acacode ##
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
* ---------------------------------------------------------------
*/
import { AxiosResponse } from 'axios'
import {
ResultOutputInt64,
ResultOutputListSearchTemplateGetListOutput,
ResultOutputSearchTemplateGetUpdateOutput,
SearchTemplateSaveInput,
} from './data-contracts'
import { ContentType, HttpClient, RequestParams } from './http-client'
export class SearchTemplateApi<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
/**
* No description
*
* @tags search-template
* @name Get
* @summary
* @request GET:/api/admin/search-template/get
* @secure
*/
get = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<ResultOutputSearchTemplateGetUpdateOutput, any>({
path: `/api/admin/search-template/get`,
method: 'GET',
query: query,
secure: true,
format: 'json',
...params,
})
/**
* No description
*
* @tags search-template
* @name GetList
* @summary
* @request GET:/api/admin/search-template/get-list
* @secure
*/
getList = (
query?: {
/** @format int64 */
moduleId?: number
},
params: RequestParams = {}
) =>
this.request<ResultOutputListSearchTemplateGetListOutput, any>({
path: `/api/admin/search-template/get-list`,
method: 'GET',
query: query,
secure: true,
format: 'json',
...params,
})
/**
* No description
*
* @tags search-template
* @name Save
* @summary
* @request POST:/api/admin/search-template/save
* @secure
*/
save = (data: SearchTemplateSaveInput, params: RequestParams = {}) =>
this.request<ResultOutputInt64, any>({
path: `/api/admin/search-template/save`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* No description
*
* @tags search-template
* @name Delete
* @summary
* @request DELETE:/api/admin/search-template/delete
* @secure
*/
delete = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<AxiosResponse, any>({
path: `/api/admin/search-template/delete`,
method: 'DELETE',
query: query,
secure: true,
...params,
})
}

View File

@ -1,181 +0,0 @@
/* eslint-disable */
/* tslint:disable */
// @ts-nocheck
/*
* ---------------------------------------------------------------
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
* ## ##
* ## AUTHOR: acacode ##
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
* ---------------------------------------------------------------
*/
import { AxiosResponse } from 'axios'
import {
PageInputSiteMsgGetPageInput,
ResultOutputBoolean,
ResultOutputPageOutputSiteMsgGetPageOutput,
ResultOutputSiteMsgGetContentOutput,
} from './data-contracts'
import { ContentType, HttpClient, RequestParams } from './http-client'
export class SiteMsgApi<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
/**
* No description
*
* @tags site-msg
* @name GetContent
* @summary
* @request GET:/api/admin/site-msg/get-content
* @secure
*/
getContent = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<ResultOutputSiteMsgGetContentOutput, any>({
path: `/api/admin/site-msg/get-content`,
method: 'GET',
query: query,
secure: true,
format: 'json',
...params,
})
/**
* No description
*
* @tags site-msg
* @name GetPage
* @summary
* @request POST:/api/admin/site-msg/get-page
* @secure
*/
getPage = (data: PageInputSiteMsgGetPageInput, params: RequestParams = {}) =>
this.request<ResultOutputPageOutputSiteMsgGetPageOutput, any>({
path: `/api/admin/site-msg/get-page`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* No description
*
* @tags site-msg
* @name IsUnread
* @summary
* @request GET:/api/admin/site-msg/is-unread
* @secure
*/
isUnread = (params: RequestParams = {}) =>
this.request<ResultOutputBoolean, any>({
path: `/api/admin/site-msg/is-unread`,
method: 'GET',
secure: true,
format: 'json',
...params,
})
/**
* No description
*
* @tags site-msg
* @name SetAllRead
* @summary
* @request POST:/api/admin/site-msg/set-all-read
* @secure
*/
setAllRead = (params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/site-msg/set-all-read`,
method: 'POST',
secure: true,
...params,
})
/**
* No description
*
* @tags site-msg
* @name SetRead
* @summary
* @request POST:/api/admin/site-msg/set-read
* @secure
*/
setRead = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<AxiosResponse, any>({
path: `/api/admin/site-msg/set-read`,
method: 'POST',
query: query,
secure: true,
...params,
})
/**
* No description
*
* @tags site-msg
* @name BatchSetRead
* @summary
* @request POST:/api/admin/site-msg/batch-set-read
* @secure
*/
batchSetRead = (data: number[], params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/site-msg/batch-set-read`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags site-msg
* @name SoftDelete
* @summary
* @request DELETE:/api/admin/site-msg/soft-delete
* @secure
*/
softDelete = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<AxiosResponse, any>({
path: `/api/admin/site-msg/soft-delete`,
method: 'DELETE',
query: query,
secure: true,
...params,
})
/**
* No description
*
* @tags site-msg
* @name BatchSoftDelete
* @summary
* @request POST:/api/admin/site-msg/batch-soft-delete
* @secure
*/
batchSoftDelete = (data: number[], params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/site-msg/batch-soft-delete`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
}

View File

@ -1,287 +0,0 @@
/* eslint-disable */
/* tslint:disable */
// @ts-nocheck
/*
* ---------------------------------------------------------------
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
* ## ##
* ## AUTHOR: acacode ##
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
* ---------------------------------------------------------------
*/
import { AxiosResponse } from 'axios'
import {
PageInputTaskGetPageInput,
ResultOutputPageOutputTaskGetPageOutput,
ResultOutputString,
ResultOutputTaskGetOutput,
TaskAddInput,
TaskUpdateInput,
} from './data-contracts'
import { ContentType, HttpClient, RequestParams } from './http-client'
export class TaskApi<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
/**
* No description
*
* @tags task
* @name GetAlerEmail
* @summary
* @request GET:/api/admin/task/get-aler-email
* @secure
*/
getAlerEmail = (
query?: {
id?: string
},
params: RequestParams = {}
) =>
this.request<ResultOutputString, any>({
path: `/api/admin/task/get-aler-email`,
method: 'GET',
query: query,
secure: true,
format: 'json',
...params,
})
/**
* No description
*
* @tags task
* @name Get
* @summary
* @request GET:/api/admin/task/get
* @secure
*/
get = (
query?: {
id?: string
},
params: RequestParams = {}
) =>
this.request<ResultOutputTaskGetOutput, any>({
path: `/api/admin/task/get`,
method: 'GET',
query: query,
secure: true,
format: 'json',
...params,
})
/**
* No description
*
* @tags task
* @name GetPage
* @summary
* @request POST:/api/admin/task/get-page
* @secure
*/
getPage = (data: PageInputTaskGetPageInput, params: RequestParams = {}) =>
this.request<ResultOutputPageOutputTaskGetPageOutput, any>({
path: `/api/admin/task/get-page`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* No description
*
* @tags task
* @name Add
* @summary
* @request POST:/api/admin/task/add
* @secure
*/
add = (data: TaskAddInput, params: RequestParams = {}) =>
this.request<ResultOutputString, any>({
path: `/api/admin/task/add`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* No description
*
* @tags task
* @name Update
* @summary
* @request PUT:/api/admin/task/update
* @secure
*/
update = (data: TaskUpdateInput, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/task/update`,
method: 'PUT',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags task
* @name Pause
* @summary
* @request POST:/api/admin/task/pause
* @secure
*/
pause = (
query: {
id: string
},
params: RequestParams = {}
) =>
this.request<AxiosResponse, any>({
path: `/api/admin/task/pause`,
method: 'POST',
query: query,
secure: true,
...params,
})
/**
* No description
*
* @tags task
* @name Resume
* @summary
* @request POST:/api/admin/task/resume
* @secure
*/
resume = (
query: {
id: string
},
params: RequestParams = {}
) =>
this.request<AxiosResponse, any>({
path: `/api/admin/task/resume`,
method: 'POST',
query: query,
secure: true,
...params,
})
/**
* No description
*
* @tags task
* @name Run
* @summary
* @request POST:/api/admin/task/run
* @secure
*/
run = (
query: {
id: string
},
params: RequestParams = {}
) =>
this.request<AxiosResponse, any>({
path: `/api/admin/task/run`,
method: 'POST',
query: query,
secure: true,
...params,
})
/**
* No description
*
* @tags task
* @name Delete
* @summary
* @request DELETE:/api/admin/task/delete
* @secure
*/
delete = (
query: {
id: string
},
params: RequestParams = {}
) =>
this.request<AxiosResponse, any>({
path: `/api/admin/task/delete`,
method: 'DELETE',
query: query,
secure: true,
...params,
})
/**
* No description
*
* @tags task
* @name BatchRun
* @summary
* @request PUT:/api/admin/task/batch-run
* @secure
*/
batchRun = (data: string[], params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/task/batch-run`,
method: 'PUT',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags task
* @name BatchPause
* @summary
* @request PUT:/api/admin/task/batch-pause
* @secure
*/
batchPause = (data: string[], params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/task/batch-pause`,
method: 'PUT',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags task
* @name BatchResume
* @summary
* @request PUT:/api/admin/task/batch-resume
* @secure
*/
batchResume = (data: string[], params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/task/batch-resume`,
method: 'PUT',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags task
* @name BatchDelete
* @summary
* @request PUT:/api/admin/task/batch-delete
* @secure
*/
batchDelete = (data: string[], params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/task/batch-delete`,
method: 'PUT',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
}

View File

@ -1,36 +0,0 @@
/* eslint-disable */
/* tslint:disable */
// @ts-nocheck
/*
* ---------------------------------------------------------------
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
* ## ##
* ## AUTHOR: acacode ##
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
* ---------------------------------------------------------------
*/
import { PageInputTaskLogGetPageInput, ResultOutputPageOutputTaskLog } from './data-contracts'
import { ContentType, HttpClient, RequestParams } from './http-client'
export class TaskLogApi<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
/**
* No description
*
* @tags task-log
* @name GetPage
* @summary
* @request POST:/api/admin/task-log/get-page
* @secure
*/
getPage = (data: PageInputTaskLogGetPageInput, params: RequestParams = {}) =>
this.request<ResultOutputPageOutputTaskLog, any>({
path: `/api/admin/task-log/get-page`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
}

View File

@ -1,152 +0,0 @@
/* eslint-disable */
/* tslint:disable */
/*
* ---------------------------------------------------------------
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
* ## ##
* ## AUTHOR: acacode ##
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
* ---------------------------------------------------------------
*/
import { ContentType, HttpClient, RequestParams } from './http-client'
export class TemplateCenterApi<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
/**
*
*
* @tags
* @name GetTree
* @request GET:/api/admin/template-center/get-tree
*/
getTree = (params: RequestParams = {}) =>
this.request<ApiResult<TemplateCenterGetTreeOutput[]>, any>({
path: `/api/admin/template-center/get-tree`,
method: 'GET',
...params,
})
/**
*
*
* @tags
* @name Add
* @request POST:/api/admin/template-center/add
*/
add = (data: TemplateCenterAddInput, params: RequestParams = {}) =>
this.request<ApiResult, any>({
path: `/api/admin/template-center/add`,
method: 'POST',
body: data,
type: ContentType.Json,
...params,
})
/**
*
*
* @tags
* @name Update
* @request PUT:/api/admin/template-center/update
*/
update = (data: TemplateCenterUpdateInput, params: RequestParams = {}) =>
this.request<ApiResult, any>({
path: `/api/admin/template-center/update`,
method: 'PUT',
body: data,
type: ContentType.Json,
...params,
})
/**
*
*
* @tags
* @name Delete
* @request DELETE:/api/admin/template-center/delete
*/
delete = (params: { id: number }, requestParams: RequestParams = {}) =>
this.request<ApiResult, any>({
path: `/api/admin/template-center/delete`,
method: 'DELETE',
query: params,
...requestParams,
})
/**
*
*
* @tags
* @name GetSharedFolderContent
* @request GET:/api/admin/template-center/get-shared-folder-content
*/
getSharedFolderContent = (
params: {
folderPath?: string
parentId?: number
includeSubfolders?: boolean
},
requestParams: RequestParams = {}
) =>
this.request<ApiResult, any>({
path: `/api/admin/template-center/get-shared-folder-content`,
method: 'GET',
query: params,
...requestParams,
})
}
/** API返回结果 */
export interface ApiResult<T = any> {
success?: boolean
code?: string
msg?: string
data?: T
}
/** 模板中心树形结构输出 */
export interface TemplateCenterGetTreeOutput {
id?: number
parentId?: number
name?: string
type?: number // 1=文件夹, 2=文件
contactPerson?: string
functionIntro?: string
keywords?: string
site?: string
filePath?: string
fileKeword?: string
fileUrl?: string
createdTime?: string
modifiedTime?: string
children?: TemplateCenterGetTreeOutput[]
}
/** 新增文件夹或文件输入 */
export interface TemplateCenterAddInput {
parentId?: number
name?: string
type?: number // 1=文件夹, 2=文件
contactPerson?: string
functionIntro?: string
keywords?: string
site?: string
filePath?: string
fileKeword?: string
fileUrl?: string
}
/** 更新文件夹或文件输入 */
export interface TemplateCenterUpdateInput {
id?: number
parentId?: number
name?: string
type?: number // 1=文件夹, 2=文件
contactPerson?: string
functionIntro?: string
keywords?: string
site?: string
filePath?: string
fileKeword?: string
fileUrl?: string
}

View File

@ -1,213 +0,0 @@
/* eslint-disable */
/* tslint:disable */
// @ts-nocheck
/*
* ---------------------------------------------------------------
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
* ## ##
* ## AUTHOR: acacode ##
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
* ---------------------------------------------------------------
*/
import { AxiosResponse } from 'axios'
import {
PageInputTenantGetPageInput,
ResultOutputInt64,
ResultOutputPageOutputTenantGetPageOutput,
ResultOutputTenantGetOutput,
ResultOutputTokenInfo,
TenantAddInput,
TenantSetEnableInput,
TenantUpdateInput,
} from './data-contracts'
import { ContentType, HttpClient, RequestParams } from './http-client'
export class TenantApi<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
/**
* No description
*
* @tags tenant
* @name Get
* @summary
* @request GET:/api/admin/tenant/get
* @secure
*/
get = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<ResultOutputTenantGetOutput, any>({
path: `/api/admin/tenant/get`,
method: 'GET',
query: query,
secure: true,
format: 'json',
...params,
})
/**
* No description
*
* @tags tenant
* @name GetPage
* @summary
* @request POST:/api/admin/tenant/get-page
* @secure
*/
getPage = (data: PageInputTenantGetPageInput, params: RequestParams = {}) =>
this.request<ResultOutputPageOutputTenantGetPageOutput, any>({
path: `/api/admin/tenant/get-page`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* No description
*
* @tags tenant
* @name Add
* @summary
* @request POST:/api/admin/tenant/add
* @secure
*/
add = (data: TenantAddInput, params: RequestParams = {}) =>
this.request<ResultOutputInt64, any>({
path: `/api/admin/tenant/add`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* No description
*
* @tags tenant
* @name Update
* @summary
* @request PUT:/api/admin/tenant/update
* @secure
*/
update = (data: TenantUpdateInput, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/tenant/update`,
method: 'PUT',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags tenant
* @name Delete
* @summary
* @request DELETE:/api/admin/tenant/delete
* @secure
*/
delete = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<AxiosResponse, any>({
path: `/api/admin/tenant/delete`,
method: 'DELETE',
query: query,
secure: true,
...params,
})
/**
* No description
*
* @tags tenant
* @name SoftDelete
* @summary
* @request DELETE:/api/admin/tenant/soft-delete
* @secure
*/
softDelete = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<AxiosResponse, any>({
path: `/api/admin/tenant/soft-delete`,
method: 'DELETE',
query: query,
secure: true,
...params,
})
/**
* No description
*
* @tags tenant
* @name BatchSoftDelete
* @summary
* @request PUT:/api/admin/tenant/batch-soft-delete
* @secure
*/
batchSoftDelete = (data: number[], params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/tenant/batch-soft-delete`,
method: 'PUT',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags tenant
* @name SetEnable
* @summary
* @request POST:/api/admin/tenant/set-enable
* @secure
*/
setEnable = (data: TenantSetEnableInput, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/tenant/set-enable`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags tenant
* @name OneClickLogin
* @summary
* @request POST:/api/admin/tenant/one-click-login
* @secure
*/
oneClickLogin = (
query?: {
/** @format int64 */
tenantId?: number
},
params: RequestParams = {}
) =>
this.request<ResultOutputTokenInfo, any>({
path: `/api/admin/tenant/one-click-login`,
method: 'POST',
query: query,
secure: true,
format: 'json',
...params,
})
}

View File

@ -1,65 +0,0 @@
/* eslint-disable */
/* tslint:disable */
// @ts-nocheck
/*
* ---------------------------------------------------------------
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
* ## ##
* ## AUTHOR: acacode ##
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
* ---------------------------------------------------------------
*/
import { AxiosResponse } from 'axios'
import { ContentType, HttpClient, RequestParams } from './http-client'
import { ResultOutputListTopClickedWebsiteOutput } from './data-contracts'
export class UserClickApi<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
/**
* No description
*
* @tags user-click
* @name RecordClick
* @summary
* @request POST:/api/admin/user-click/record-click
* @secure
*/
recordClick = (
data: {
/** 网页类型 1:在线应用 3:模板中心 5:在线看板 */
webType: number
/** 网页Id */
webId: number
},
params: RequestParams = {}
) =>
this.request<AxiosResponse, any>({
path: `/api/admin/user-click/record-click`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* No description
*
* @tags user-click
* @name GetTopClickedWebsites
* @summary
* @request POST:/api/admin/user-click/get-top-clicked-websites
* @secure
*/
getTopClickedWebsites = (
params: RequestParams = {}
) =>
this.request<ResultOutputListTopClickedWebsiteOutput, any>({
path: `/api/admin/user-click/get-top-clicked-websites`,
method: 'POST',
secure: true,
format: 'json',
...params,
})
}

View File

@ -1,206 +0,0 @@
/* eslint-disable */
/* tslint:disable */
// @ts-nocheck
/*
* ---------------------------------------------------------------
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
* ## ##
* ## AUTHOR: acacode ##
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
* ---------------------------------------------------------------
*/
import { AxiosResponse } from 'axios'
import {
ResultOutputInt64,
ResultOutputListViewGetListOutput,
ResultOutputViewGetOutput,
ViewAddInput,
ViewGetListInput,
ViewSyncInput,
ViewUpdateInput,
} from './data-contracts'
import { ContentType, HttpClient, RequestParams } from './http-client'
export class ViewApi<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
/**
* No description
*
* @tags view
* @name Get
* @summary
* @request GET:/api/admin/view/get
* @secure
*/
get = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<ResultOutputViewGetOutput, any>({
path: `/api/admin/view/get`,
method: 'GET',
query: query,
secure: true,
format: 'json',
...params,
})
/**
* No description
*
* @tags view
* @name GetList
* @summary
* @request POST:/api/admin/view/get-list
* @secure
*/
getList = (data: ViewGetListInput, params: RequestParams = {}) =>
this.request<ResultOutputListViewGetListOutput, any>({
path: `/api/admin/view/get-list`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* No description
*
* @tags view
* @name Add
* @summary
* @request POST:/api/admin/view/add
* @secure
*/
add = (data: ViewAddInput, params: RequestParams = {}) =>
this.request<ResultOutputInt64, any>({
path: `/api/admin/view/add`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
format: 'json',
...params,
})
/**
* No description
*
* @tags view
* @name Update
* @summary
* @request PUT:/api/admin/view/update
* @secure
*/
update = (data: ViewUpdateInput, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/view/update`,
method: 'PUT',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags view
* @name Delete
* @summary
* @request DELETE:/api/admin/view/delete
* @secure
*/
delete = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<AxiosResponse, any>({
path: `/api/admin/view/delete`,
method: 'DELETE',
query: query,
secure: true,
...params,
})
/**
* No description
*
* @tags view
* @name BatchDelete
* @summary
* @request PUT:/api/admin/view/batch-delete
* @secure
*/
batchDelete = (data: number[], params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/view/batch-delete`,
method: 'PUT',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags view
* @name SoftDelete
* @summary
* @request DELETE:/api/admin/view/soft-delete
* @secure
*/
softDelete = (
query?: {
/** @format int64 */
id?: number
},
params: RequestParams = {}
) =>
this.request<AxiosResponse, any>({
path: `/api/admin/view/soft-delete`,
method: 'DELETE',
query: query,
secure: true,
...params,
})
/**
* No description
*
* @tags view
* @name BatchSoftDelete
* @summary
* @request PUT:/api/admin/view/batch-soft-delete
* @secure
*/
batchSoftDelete = (data: number[], params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/view/batch-soft-delete`,
method: 'PUT',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
/**
* No description
*
* @tags view
* @name Sync
* @summary
* @request POST:/api/admin/view/sync
* @secure
*/
sync = (data: ViewSyncInput, params: RequestParams = {}) =>
this.request<AxiosResponse, any>({
path: `/api/admin/view/sync`,
method: 'POST',
body: data,
secure: true,
type: ContentType.Json,
...params,
})
}

View File

@ -1,165 +0,0 @@
<template>
<div>
<el-dialog
v-model="state.showDialog"
destroy-on-close
:title="title"
draggable
:close-on-click-modal="false"
:close-on-press-escape="false"
width="600px"
>
<el-form :model="form" ref="formRef" label-width="80px">
<el-row :gutter="35">
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="上级接口">
<el-tree-select
v-model="form.parentId"
:data="apiTreeData"
node-key="id"
check-strictly
default-expand-all
render-after-expand
fit-input-width
clearable
class="w100"
/>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="接口名称" prop="label" :rules="[{ required: true, message: '请输入接口名称', trigger: ['blur', 'change'] }]">
<el-input v-model="form.label" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="接口地址" prop="path" :rules="[{ required: true, message: '请输入接口地址', trigger: ['blur', 'change'] }]">
<el-input v-model="form.path" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="接口方法" prop="httpMethods">
<el-radio-group v-model="form.httpMethods">
<el-radio-button label="get" />
<el-radio-button label="put" />
<el-radio-button label="post" />
<el-radio-button label="patch" />
<el-radio-button label="delete" />
</el-radio-group>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="接口描述" prop="description">
<el-input v-model="form.description" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="排序">
<el-input-number v-model="form.sort" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="启用">
<el-switch v-model="form.enabled" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel"> </el-button>
<el-button type="primary" @click="onSure" :loading="state.sureLoading"> </el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup>
import { reactive, toRefs, ref, PropType } from 'vue'
import { ApiGetListOutput, ApiUpdateInput } from '/@/api/admin/data-contracts'
import { ApiApi } from '/@/api/admin/Api'
import eventBus from '/@/utils/mitt'
defineProps({
title: {
type: String,
default: '',
},
apiTreeData: {
type: Array as PropType<ApiGetListOutput[]>,
default: () => [],
},
})
const formRef = ref()
const state = reactive({
showDialog: false,
sureLoading: false,
form: {
enabled: true,
} as ApiUpdateInput,
})
const { form } = toRefs(state)
//
const open = async (row: any = {}) => {
if (row.id > 0) {
const res = await new ApiApi().get({ id: row.id }, { loading: true })
if (res?.success) {
let formData = res.data as ApiUpdateInput
formData.parentId = formData.parentId && formData.parentId > 0 ? formData.parentId : undefined
state.form = formData
}
} else {
state.form = {
enabled: true,
} as ApiUpdateInput
}
state.showDialog = true
}
//
const onCancel = () => {
state.showDialog = false
}
//
const onSure = () => {
formRef.value.validate(async (valid: boolean) => {
if (!valid) return
state.sureLoading = true
let res = {} as any
state.form.parentId = state.form.parentId && state.form.parentId > 0 ? state.form.parentId : undefined
if (state.form.id != undefined && state.form.id > 0) {
res = await new ApiApi().update(state.form, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
} else {
res = await new ApiApi().add(state.form, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
}
state.sureLoading = false
if (res?.success) {
eventBus.emit('refreshApi')
state.showDialog = false
}
})
}
defineExpose({
open,
})
</script>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'admin/api/form',
})
</script>

View File

@ -1,362 +0,0 @@
<template>
<my-layout>
<el-card class="my-query-box mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
<el-form :inline="true" @submit.stop.prevent>
<el-form-item label="接口名称">
<el-input v-model="state.filter.name" placeholder="接口名称" @keyup.enter="onQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="ele-Search" @click="onQuery"> 查询 </el-button>
<el-button v-auth="'api:admin:api:add'" type="primary" icon="ele-Plus" @click="onAdd"> 新增 </el-button>
<el-popconfirm title="确定要同步接口" hide-icon width="180" hide-after="0" @confirm="onSync">
<template #reference>
<el-button v-auth="'api:admin:api:sync'" :loading="state.syncLoading" type="primary" icon="ele-Refresh"> 同步 </el-button>
</template>
</el-popconfirm>
</el-form-item>
</el-form>
</el-card>
<el-card class="my-fill mt8" shadow="never">
<el-table
:data="state.apiTreeData"
style="width: 100%"
v-loading="state.loading"
row-key="id"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
:expand-row-keys="state.expandRowKeys"
border
>
<el-table-column prop="label" label="接口名称" min-width="120" show-overflow-tooltip />
<el-table-column prop="path" label="接口地址" min-width="120" show-overflow-tooltip>
<template #default="{ row }">
<el-tag v-if="row.httpMethods" :type="getTagTypeByHttpMethod(row.httpMethods)" style="width: 54px">{{ row.httpMethods }}</el-tag>
{{ row.path }}
</template>
</el-table-column>
<el-table-column label="请求日志" width="90" align="center">
<template #default="{ row }">
<el-switch
v-if="row.httpMethods"
v-model="row.enabledLog"
:loading="row.loadingEnabledLog"
:active-value="true"
:inactive-value="false"
inline-prompt
active-text="启用"
inactive-text="禁用"
:before-change="() => onSetEnableLog(row)"
/>
</template>
</el-table-column>
<el-table-column label="请求参数" width="90" align="center">
<template #default="{ row }">
<el-switch
v-if="row.httpMethods"
v-model="row.enabledParams"
:loading="row.loadingEnabledParams"
:active-value="true"
:inactive-value="false"
inline-prompt
active-text="启用"
inactive-text="禁用"
:before-change="() => onSetEnableParams(row)"
/>
</template>
</el-table-column>
<el-table-column label="响应结果" width="90" align="center">
<template #default="{ row }">
<el-switch
v-if="row.httpMethods"
v-model="row.enabledResult"
:loading="row.loadingEnabledResult"
:active-value="true"
:inactive-value="false"
inline-prompt
active-text="启用"
inactive-text="禁用"
:before-change="() => onSetEnableResult(row)"
/>
</template>
</el-table-column>
<el-table-column prop="sort" label="排序" width="82" align="center" show-overflow-tooltip />
<el-table-column label="状态" width="82" align="center">
<template #default="{ row }">
<el-tag type="success" v-if="row.enabled">启用</el-tag>
<el-tag type="danger" v-else>禁用</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="160" fixed="right" header-align="center" align="center">
<template #default="{ row }">
<el-button v-auth="'api:admin:api:update'" icon="ele-EditPen" text type="primary" @click="onEdit(row)">编辑</el-button>
<el-button v-auth="'api:admin:api:delete'" icon="ele-Delete" text type="danger" @click="onDelete(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
<api-form ref="apiFormRef" :title="state.apiFormTitle" :api-tree-data="state.formApiTreeData"></api-form>
</my-layout>
</template>
<script lang="ts" setup name="admin/api">
import { ref, reactive, onMounted, getCurrentInstance, onBeforeMount, defineAsyncComponent } from 'vue'
import { ApiGetListOutput } from '/@/api/admin/data-contracts'
import { ApiApi } from '/@/api/admin/Api'
import { ApiApi as ApiExtApi } from '/@/api/admin.extend/Api'
import { listToTree, treeToList, filterTree, filterList } from '/@/utils/tree'
import { cloneDeep, isArray } from 'lodash-es'
import eventBus from '/@/utils/mitt'
//
const ApiForm = defineAsyncComponent(() => import('./components/api-form.vue'))
const { proxy } = getCurrentInstance() as any
const apiFormRef = ref()
const state = reactive({
loading: false,
syncLoading: false,
apiFormTitle: '',
filter: {
name: '',
},
apiTreeData: [] as Array<ApiGetListOutput>,
formApiTreeData: [] as Array<ApiGetListOutput>,
expandRowKeys: [] as string[],
})
onMounted(async () => {
await onQuery()
state.expandRowKeys = treeToList(cloneDeep(state.apiTreeData))
.filter((a: ApiGetListOutput) => a.parentId === 0)
.map((a: ApiGetListOutput) => a.id + '') as string[]
eventBus.off('refreshApi')
eventBus.on('refreshApi', async () => {
onQuery()
})
})
onBeforeMount(() => {
eventBus.off('refreshApi')
})
const getTagTypeByHttpMethod = (httpMethods: string) => {
const methods = httpMethods.toLowerCase().split(/\s+/)
if (methods.some((method) => method === 'get')) {
return 'success'
}
if (methods.some((method) => method === 'delete')) {
return 'danger'
}
if (methods.some((method) => method === 'patch')) {
return 'info'
}
return 'primary'
}
//
const onSetEnableLog = (row: ApiGetListOutput & { loadingEnabledLog: boolean; loadingEnabledParams: boolean; loadingEnabledResult: boolean }) => {
return new Promise((resolve, reject) => {
proxy.$modal
.confirm(`确定要${row.enabledLog ? '禁用' : '启用'}${row.label}】请求参数?`)
.then(async () => {
row.loadingEnabledLog = true
const res = await new ApiApi()
.setEnableLog({ apiId: row.id, enabledLog: !row.enabledLog }, { showSuccessMessage: true })
.catch(() => {
reject(new Error('Error'))
})
.finally(() => {
row.loadingEnabledLog = false
})
if (res && res.success) {
resolve(true)
} else {
reject(new Error('Cancel'))
}
})
.catch(() => {
reject(new Error('Cancel'))
})
})
}
//
const onSetEnableParams = (row: ApiGetListOutput & { loadingEnabledLog: boolean; loadingEnabledParams: boolean; loadingEnabledResult: boolean }) => {
return new Promise((resolve, reject) => {
proxy.$modal
.confirm(`确定要${row.enabledParams ? '禁用' : '启用'}${row.label}】请求参数?`)
.then(async () => {
row.loadingEnabledParams = true
const res = await new ApiApi()
.setEnableParams({ apiId: row.id, enabledParams: !row.enabledParams }, { showSuccessMessage: true })
.catch(() => {
reject(new Error('Error'))
})
.finally(() => {
row.loadingEnabledParams = false
})
if (res && res.success) {
resolve(true)
} else {
reject(new Error('Cancel'))
}
})
.catch(() => {
reject(new Error('Cancel'))
})
})
}
//
const onSetEnableResult = (row: ApiGetListOutput & { loadingEnabledLog: boolean; loadingEnabledParams: boolean; loadingEnabledResult: boolean }) => {
return new Promise((resolve, reject) => {
proxy.$modal
.confirm(`确定要${row.enabledResult ? '禁用' : '启用'}${row.label}】响应结果?`)
.then(async () => {
row.loadingEnabledResult = true
const res = await new ApiApi()
.setEnableResult({ apiId: row.id, enabledResult: !row.enabledResult }, { showSuccessMessage: true })
.catch(() => {
reject(new Error('Error'))
})
.finally(() => {
row.loadingEnabledResult = false
})
if (res && res.success) {
resolve(true)
} else {
reject(new Error('Cancel'))
}
})
.catch(() => {
reject(new Error('Cancel'))
})
})
}
const onQuery = async () => {
state.loading = true
const res = await new ApiApi().getList().catch(() => {
state.loading = false
})
if (res && res.data && res.data.length > 0) {
state.apiTreeData = filterTree(listToTree(cloneDeep(res.data)), state.filter.name, {
filterWhere: (item: any, keyword: string) => {
return item.label?.toLocaleLowerCase().indexOf(keyword) > -1 || item.path?.toLocaleLowerCase().indexOf(keyword) > -1
},
})
//#57
state.formApiTreeData = listToTree(
filterList(cloneDeep(res.data), '', {
filterWhere: (item: any, word: string) => {
return !item.httpMethods
},
})
)
} else {
state.apiTreeData = []
state.formApiTreeData = []
}
state.loading = false
}
const onAdd = () => {
state.apiFormTitle = '新增接口'
apiFormRef.value.open()
}
const onEdit = (row: ApiGetListOutput) => {
state.apiFormTitle = '编辑接口'
apiFormRef.value.open(row)
}
const onDelete = (row: ApiGetListOutput) => {
proxy.$modal
.confirmDelete(`确定要删除接口【${row.label}】?`)
.then(async () => {
await new ApiApi().delete({ id: row.id }, { loading: true })
onQuery()
})
.catch(() => {})
}
const syncApi = async (swaggerResource: any) => {
const res = await new ApiExtApi().getSwaggerJson(swaggerResource.url, { showErrorMessage: false })
if (!res) {
return
}
const tags = res.tags
const paths = res.paths
const apis = []
const urls = swaggerResource.url.split('/')
const code = urls.length >= 2 ? urls[urls.length - 2] : ''
if (code === '') {
return
}
apis[apis.length] = {
label: swaggerResource.name,
path: code,
}
// tags
if (tags && tags.length > 0) {
tags.forEach((t: any) => {
apis[apis.length] = {
label: t.description,
path: t.name,
parentPath: code,
}
})
}
// paths
if (paths) {
for (const [key, value] of Object.entries(paths)) {
const keys = Object.keys(value as any)
const values = Object.values(value as any)
const v = values && values.length > 0 ? values[0] : ({} as any)
const parentPath = v.tags && v.tags.length > 0 ? v.tags[0] : ''
apis[apis.length] = {
label: v.summary,
path: key,
parentPath,
httpMethods: keys.join(','),
}
}
}
return await new ApiApi().sync({ apis })
}
const onSync = async () => {
state.syncLoading = true
const swaggerResourcePaths = ['doc/admin']
//const swaggerResourcePaths = ['doc/admin']
// const swaggerResourcePaths = ['doc/app']
const swaggerResourceUrls = swaggerResourcePaths?.map((path) => `/${path}/swagger-resources`) as string[]
const lastSwaggerResourcesIndex = swaggerResourceUrls.length - 1
swaggerResourceUrls.forEach(async (swaggerResourceUrl, swaggerResourcesIndex) => {
const resSwaggerResources = await new ApiExtApi().getSwaggerResources(swaggerResourceUrl, { showErrorMessage: false }).catch(() => {
state.syncLoading = false
})
if (isArray(resSwaggerResources) && (resSwaggerResources?.length as number) > 0) {
for (let index = 0, len = resSwaggerResources.length; index < len; index++) {
const swaggerResource = resSwaggerResources[index]
await syncApi(swaggerResource).catch(() => {
proxy.$modal.msgSuccess(`同步${swaggerResource.name}失败`)
})
}
}
if (swaggerResourcesIndex === lastSwaggerResourcesIndex) {
state.syncLoading = false
proxy.$modal.msgSuccess(`同步完成`)
onQuery()
}
})
}
</script>
<style scoped lang="scss"></style>

View File

@ -1,148 +0,0 @@
<template>
<div>
<el-dialog
v-model="state.showDialog"
destroy-on-close
:title="title"
draggable
:close-on-click-modal="false"
:close-on-press-escape="false"
width="600px"
>
<el-form :model="form" ref="formRef" label-width="80px">
<el-row :gutter="35">
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="看板名称" prop="name" :rules="[{ required: true, message: '请输入看板名称', trigger: ['blur', 'change'] }]">
<el-input v-model="form.name" clearable placeholder="请输入看板名称" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="功能简介" prop="description">
<el-input v-model="form.description" type="textarea" :rows="3" clearable placeholder="请输入功能简介" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="关键词" prop="keywords">
<el-input v-model="form.keywords" clearable placeholder="请输入关键词,多个关键词用逗号分隔" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="网址" prop="url">
<el-input v-model="form.url" clearable placeholder="请输入网址" />
</el-form-item>
</el-col>
<!-- safetyVerification -->
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="安全认证" prop="safety">
<el-switch v-model="form.safety" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="排序">
<el-input-number v-model="form.sort" :min="0" :max="9999" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="启用">
<el-switch v-model="form.enabled" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel"> </el-button>
<el-button type="primary" @click="onSure" :loading="state.sureLoading"> </el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup>
import { reactive, toRefs, ref, PropType } from 'vue'
import { BoardGetPageOutput, BoardUpdateInput } from '/@/api/admin/data-contracts'
import { BoardApi } from '/@/api/admin/Board'
import eventBus from '/@/utils/mitt'
defineProps({
title: {
type: String,
default: '',
},
})
const formRef = ref()
const state = reactive({
showDialog: false,
sureLoading: false,
form: {
enabled: true,
sort: 0,
} as BoardUpdateInput,
})
const { form } = toRefs(state)
//
const open = async (row: any = {}) => {
if (row.id > 0) {
const res = await new BoardApi().get({ id: row.id }, { loading: true })
if (res?.success) {
let formData = res.data as BoardUpdateInput
state.form = formData
}
} else {
state.form = {
enabled: true,
sort: 0,
} as BoardUpdateInput
}
state.showDialog = true
}
//
const onCancel = () => {
state.showDialog = false
}
//
const onSure = () => {
formRef.value.validate(async (valid: boolean) => {
if (!valid) return
state.sureLoading = true
let res = {} as any
if (state.form.id != undefined && state.form.id > 0) {
res = await new BoardApi().update(state.form, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
} else {
res = await new BoardApi().add(state.form, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
}
state.sureLoading = false
if (res?.success) {
eventBus.emit('refreshBoard')
state.showDialog = false
}
})
}
defineExpose({
open,
})
</script>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'admin/board/form',
})
</script>

View File

@ -1,329 +0,0 @@
<template>
<MySplitPanes>
<pane size="55" min-size="35" max-size="65">
<div class="my-flex-column w100 h100">
<el-card class="my-query-box mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
<el-form :inline="true" @submit.stop.prevent>
<el-form-item label="看板名称">
<el-input v-model="state.filter.name" placeholder="看板名称" @keyup.enter="onQuery" />
</el-form-item>
<el-form-item label="关键词">
<el-input v-model="state.filter.keywords" placeholder="关键词" @keyup.enter="onQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="ele-Search" @click="onQuery"> 查询 </el-button>
<el-button v-auth="'api:admin:board:add'" type="primary" icon="ele-Plus" @click="onAdd"> 新增 </el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="my-fill mt8" shadow="never">
<el-table
ref="boardTableRef"
v-loading="state.loading"
:data="state.boardListData"
default-expand-all
highlight-current-row
style="width: 100%"
@current-change="onTableCurrentChange"
border
>
<el-table-column prop="name" label="看板名称" min-width="120" show-overflow-tooltip />
<el-table-column prop="description" label="功能简介" min-width="150" show-overflow-tooltip />
<el-table-column prop="keywords" label="关键词" min-width="120" show-overflow-tooltip />
<el-table-column prop="url" label="网址" min-width="150" show-overflow-tooltip>
<template #default="{ row }">
<el-link v-if="row.url" :href="row.url" target="_blank" type="primary">{{ row.url }}</el-link>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column prop="sort" label="排序" width="90" align="center" sortable="custom" show-overflow-tooltip />
<el-table-column label="状态" width="82" align="center">
<template #default="{ row }">
<el-tag type="success" v-if="row.enabled">启用</el-tag>
<el-tag type="danger" v-else>禁用</el-tag>
</template>
</el-table-column>
<el-table-column prop="createdTime" label="创建时间" width="160" align="center" show-overflow-tooltip />
<el-table-column label="操作" width="140" fixed="right" header-align="center" align="center">
<template #default="{ row }">
<el-button v-auth="'api:admin:board:update'" icon="ele-EditPen" text type="primary" @click="onEdit(row)">编辑</el-button>
<el-button v-auth="'api:admin:board:delete'" icon="ele-Delete" text type="danger" @click="onDelete(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="my-flex my-flex-end" style="margin-top: 10px">
<el-pagination
v-model:currentPage="state.pageInput.currentPage"
v-model:page-size="state.pageInput.pageSize"
:total="state.total"
:page-sizes="[10, 20, 50, 100]"
background
@size-change="onSizeChange"
@current-change="onCurrentChange"
layout="total, sizes, prev, pager, next, jumper"
/>
</div>
</el-card>
</div>
</pane>
<pane>
<div class="my-flex-column w100 h100">
<el-card class="my-query-box mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
<el-form :inline="true" @submit.stop.prevent>
<el-form-item label="姓名">
<el-input v-model="state.userFilter.name" placeholder="姓名" @keyup.enter="onGetBoardUserList" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="ele-Search" @click="onGetBoardUserList"> 查询 </el-button>
<el-button v-auth="'api:admin:board:add-board-user'" type="primary" icon="ele-Plus" @click="onAddUser"> 添加 </el-button>
<el-button v-auth="'api:admin:board:remove-board-user'" type="danger" icon="ele-Delete" @click="onRemoveUser"> 移除 </el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="my-fill mt8" shadow="never">
<el-table
ref="userTableRef"
v-loading="state.userListLoading"
:data="state.userListData"
:row-key="getRowKey"
style="width: 100%"
border
>
<el-table-column type="selection" width="55" />
<el-table-column prop="name" label="姓名" min-width="120" show-overflow-tooltip />
<el-table-column prop="mobile" label="手机号" min-width="120" show-overflow-tooltip />
<el-table-column prop="email" label="邮箱" min-width="180" show-overflow-tooltip />
</el-table>
</el-card>
</div>
</pane>
<board-form ref="boardFormRef" :title="state.boardFormTitle"></board-form>
<user-select
ref="userSelectRef"
:title="`添加【${state.boardName}】人员`"
multiple
:sure-loading="state.sureLoading"
@sure="onSureUser"
></user-select>
</MySplitPanes>
</template>
<script lang="ts" setup name="admin/board">
import { ref, reactive, onMounted, getCurrentInstance, onBeforeMount, defineAsyncComponent } from 'vue'
import { BoardGetPageOutput, PageInputBoardGetPageInput, SortInput, BoardGetBoardUserListOutput, BoardAddBoardUserListInput, BoardRemoveBoardUserInput, UserGetPageOutput } from '/@/api/admin/data-contracts'
import { BoardApi } from '/@/api/admin/Board'
import eventBus from '/@/utils/mitt'
import { Pane } from 'splitpanes'
import { ElTable } from 'element-plus'
import dayjs from 'dayjs'
//
const BoardForm = defineAsyncComponent(() => import('./components/board-form.vue'))
const UserSelect = defineAsyncComponent(() => import('/@/views/admin/user/components/user-select.vue'))
const MySplitPanes = defineAsyncComponent(() => import('/@/components/my-layout/split-panes.vue'))
const { proxy } = getCurrentInstance() as any
const boardTableRef = ref()
const boardFormRef = ref()
const userTableRef = ref<InstanceType<typeof ElTable>>()
const userSelectRef = ref()
const defalutSort = { prop: 'sort', order: 'ascending' }
const getSortList = (data: { prop: string; order: any }) => {
return [
{
propName: data.prop,
order: data.order === 'ascending' ? 0 : data.order === 'descending' ? 1 : undefined,
},
] as [SortInput]
}
const state = reactive({
loading: false,
userListLoading: false,
sureLoading: false,
boardFormTitle: '',
filter: {
name: '',
keywords: '',
},
defalutSort: defalutSort,
total: 0,
pageInput: {
currentPage: 1,
pageSize: 20,
sortList: getSortList(defalutSort),
} as PageInputBoardGetPageInput,
boardListData: [] as Array<BoardGetPageOutput>,
userFilter: {
name: ''
},
userListData: [] as BoardGetBoardUserListOutput[],
boardId: undefined as number | undefined,
boardName: '' as string | null | undefined,
})
onMounted(async () => {
await onQuery()
eventBus.off('refreshBoard')
eventBus.on('refreshBoard', () => {
onQuery()
})
})
onBeforeMount(() => {
eventBus.off('refreshBoard')
})
const formatterTime = (row: any, column: any, cellValue: any) => {
return cellValue ? dayjs(cellValue).format('YYYY-MM-DD HH:mm:ss') : ''
}
// 使
const getRowKey = (row: BoardGetBoardUserListOutput, index: number) => {
return `user_${state.boardId}_${row.userId}_${index}`
}
const onQuery = async () => {
state.loading = true
state.pageInput.filter = state.filter
const res = await new BoardApi().getPage(state.pageInput).catch(() => {
state.loading = false
})
state.boardListData = res?.data?.list ?? []
state.total = res?.data?.total ?? 0
if (state.boardListData?.length > 0) {
window.setTimeout(() => {
boardTableRef.value?.setCurrentRow(state.boardListData[0])
}, 100)
}
state.loading = false
}
const onAdd = () => {
state.boardFormTitle = '新增看板'
boardFormRef.value.open()
}
const onEdit = (row: BoardGetPageOutput) => {
state.boardFormTitle = '编辑看板'
boardFormRef.value.open(row)
}
const onDelete = (row: BoardGetPageOutput) => {
proxy.$modal
.confirmDelete(`确定要删除【${row.name}】?`)
.then(async () => {
await new BoardApi().softDelete({ id: row.id }, { loading: true, showSuccessMessage: true })
onQuery()
})
.catch(() => {})
}
const onSizeChange = (val: number) => {
state.pageInput.pageSize = val
onQuery()
}
const onCurrentChange = (val: number) => {
state.pageInput.currentPage = val
onQuery()
}
const onSortChange = (data: { column: any; prop: string; order: any }) => {
state.pageInput.sortList = getSortList(data)
onQuery()
}
const onTableCurrentChange = (currentRow: BoardGetPageOutput) => {
if (!currentRow) {
return
}
state.boardId = currentRow.id
state.boardName = currentRow.name
onGetBoardUserList()
}
const onGetBoardUserList = async () => {
state.userListLoading = true
//
userTableRef.value?.clearSelection()
const res = await new BoardApi().getBoardUserList({ BoardId: state.boardId, Name: state.userFilter.name }).catch(() => {
state.userListLoading = false
})
state.userListLoading = false
if (res?.success) {
if (res.data && res.data.length > 0) {
state.userListData = res.data
} else {
state.userListData = []
}
}
}
const onAddUser = () => {
if (!((state.boardId as number) > 0)) {
proxy.$modal.msgWarning('请选择看板')
return
}
userSelectRef.value.open({ boardId: state.boardId })
}
const onRemoveUser = () => {
if (!((state.boardId as number) > 0)) {
proxy.$modal.msgWarning('请选择看板')
return
}
const selectionRows = userTableRef.value!.getSelectionRows() as BoardGetBoardUserListOutput[]
console.log("🔍 ~ ~ src/views/admin/board/index.vue:286 ~ selectionRows:", selectionRows)
if (!((selectionRows.length as number) > 0)) {
proxy.$modal.msgWarning('请选择人员')
return
}
proxy.$modal
.confirm(`确定要移除吗?`)
.then(async () => {
const userIds = selectionRows?.map((a) => a.userId)
const input = { boardId: state.boardId, userIds } as BoardRemoveBoardUserInput
await new BoardApi().removeBoardUser(input, { loading: true })
onGetBoardUserList()
})
.catch(() => {})
}
const onSureUser = async (users: UserGetPageOutput[]) => {
if (!(users?.length > 0)) {
userSelectRef.value.close()
return
}
state.sureLoading = true
const userIds = users?.map((a) => a.id)
const input = { boardId: state.boardId, userIds } as BoardAddBoardUserListInput
await new BoardApi().addBoardUser(input, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
state.sureLoading = false
userSelectRef.value.close()
onGetBoardUserList()
}
</script>
<style scoped lang="scss"></style>

View File

@ -1,60 +0,0 @@
<template>
<my-layout>
<el-card class="my-fill mt8" shadow="never">
<el-table v-loading="state.loading" :data="state.cacheListData" row-key="id" style="width: 100%" border>
<el-table-column type="index" width="82" label="#" />
<el-table-column prop="description" label="缓存名" />
<el-table-column prop="name" label="键名" />
<el-table-column prop="value" label="键值" />
<el-table-column label="操作" width="180" fixed="right" header-align="center" align="center">
<template #default="{ row }">
<el-button v-auth="'api:admin:cache:clear'" icon="ele-Brush" text type="danger" @click="onClear(row)">清除</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
</my-layout>
</template>
<script lang="ts" setup name="admin/cache">
import { reactive, onMounted, getCurrentInstance } from 'vue'
import { CacheApi } from '/@/api/admin/Cache'
const { proxy } = getCurrentInstance() as any
defineProps({
title: {
type: String,
default: '',
},
})
const state = reactive({
loading: false,
cacheListData: [] as any,
})
onMounted(() => {
onQuery()
})
const onQuery = async () => {
state.loading = true
const res = await new CacheApi().getList().catch(() => {
state.loading = false
})
state.cacheListData = res?.data ?? []
state.loading = false
}
const onClear = (row: any) => {
proxy.$modal
.confirmDelete(`确定要清除【${row.description}】缓存?`, { icon: 'ele-Brush' })
.then(async () => {
await new CacheApi().clear({ cacheKey: row.value }, { loading: true, showSuccessMessage: true })
onQuery()
})
.catch(() => {})
}
</script>
<style scoped lang="scss"></style>

View File

@ -1,139 +0,0 @@
<template>
<div>
<el-dialog
v-model="state.showDialog"
destroy-on-close
:title="title"
draggable
:close-on-click-modal="false"
:close-on-press-escape="false"
width="769px"
>
<el-form ref="formRef" :model="form" label-width="80px">
<el-row :gutter="35">
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="名称" prop="name" :rules="[{ required: true, message: '请输入名称', trigger: ['blur', 'change'] }]">
<el-input v-model="form.name" autocomplete="off" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="编码" prop="code">
<el-input v-model="form.code" autocomplete="off" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="字典值" prop="value">
<el-input v-model="form.value" autocomplete="off" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="排序" prop="sort">
<el-input-number v-model="form.sort" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="启用" prop="enabled">
<el-switch v-model="form.enabled" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="说明" prop="description">
<el-input v-model="form.description" clearable type="textarea" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer my-flex my-flex-y-center my-flex-between">
<div>
<el-checkbox v-if="!(state.form?.id > 0)" v-model="state.contiAdd">连续新增</el-checkbox>
</div>
<div>
<el-button @click="onCancel"> </el-button>
<el-button type="primary" @click="onSure" :loading="state.sureLoading"> </el-button>
</div>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup name="admin/dict/form">
import { reactive, toRefs, getCurrentInstance, ref } from 'vue'
import { DictAddInput, DictUpdateInput } from '/@/api/admin/data-contracts'
import { DictApi } from '/@/api/admin/Dict'
import eventBus from '/@/utils/mitt'
import { FormInstance } from 'element-plus'
defineProps({
title: {
type: String,
default: '',
},
})
const { proxy } = getCurrentInstance() as any
const formRef = ref<FormInstance>()
const state = reactive({
showDialog: false,
sureLoading: false,
form: {} as DictAddInput & DictUpdateInput,
contiAdd: false,
})
const { form } = toRefs(state)
//
const open = async (row: any = {}) => {
if (row.id > 0) {
state.contiAdd = false
const res = await new DictApi().get({ id: row.id }, { loading: true }).catch(() => {
proxy.$modal.closeLoading()
})
if (res?.success) {
state.form = res.data as DictAddInput & DictUpdateInput
}
} else {
state.form = { dictTypeId: row.dictTypeId, enabled: true } as DictAddInput & DictUpdateInput
}
state.showDialog = true
}
//
const onCancel = () => {
state.showDialog = false
}
//
const onSure = () => {
formRef.value!.validate(async (valid: boolean) => {
if (!valid) return
state.sureLoading = true
let res = {} as any
if (state.form.id != undefined && state.form.id > 0) {
res = await new DictApi().update(state.form, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
} else {
res = await new DictApi().add(state.form, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
}
state.sureLoading = false
if (res?.success) {
if (state.contiAdd) {
formRef.value!.resetFields()
}
eventBus.emit('refreshDict')
state.showDialog = state.contiAdd
}
})
}
defineExpose({
open,
})
</script>

View File

@ -1,123 +0,0 @@
<template>
<div>
<el-dialog
v-model="state.showDialog"
destroy-on-close
:title="title"
draggable
:close-on-click-modal="false"
:close-on-press-escape="false"
width="769px"
>
<el-form ref="formRef" :model="form" label-width="80px">
<el-row :gutter="35">
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="名称" prop="name" :rules="[{ required: true, message: '请输入名称', trigger: ['blur', 'change'] }]">
<el-input v-model="form.name" autocomplete="off" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="编码" prop="code" :rules="[{ required: true, message: '请输入编码', trigger: ['blur', 'change'] }]">
<el-input v-model="form.code" autocomplete="off" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="排序">
<el-input-number v-model="form.sort" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="启用">
<el-switch v-model="form.enabled" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="说明">
<el-input v-model="form.description" clearable type="textarea" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel"> </el-button>
<el-button type="primary" @click="onSure" :loading="state.sureLoading"> </el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup name="admin/dictType/form">
import { reactive, toRefs, getCurrentInstance, ref } from 'vue'
import { DictTypeAddInput, DictTypeUpdateInput } from '/@/api/admin/data-contracts'
import { DictTypeApi } from '/@/api/admin/DictType'
import eventBus from '/@/utils/mitt'
defineProps({
title: {
type: String,
default: '',
},
})
const { proxy } = getCurrentInstance() as any
const formRef = ref()
const state = reactive({
showDialog: false,
sureLoading: false,
form: {} as DictTypeAddInput & DictTypeUpdateInput,
})
const { form } = toRefs(state)
//
const open = async (row: any = {}) => {
if (row.id > 0) {
const res = await new DictTypeApi().get({ id: row.id }, { loading: true }).catch(() => {
proxy.$modal.closeLoading()
})
if (res?.success) {
state.form = res.data as DictTypeAddInput & DictTypeUpdateInput
}
} else {
state.form = { enabled: true } as DictTypeAddInput & DictTypeUpdateInput
}
state.showDialog = true
}
//
const onCancel = () => {
state.showDialog = false
}
//
const onSure = () => {
formRef.value.validate(async (valid: boolean) => {
if (!valid) return
state.sureLoading = true
let res = {} as any
if (state.form.id != undefined && state.form.id > 0) {
res = await new DictTypeApi().update(state.form, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
} else {
res = await new DictTypeApi().add(state.form, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
}
state.sureLoading = false
if (res?.success) {
eventBus.emit('refreshDictType')
state.showDialog = false
}
})
}
defineExpose({
open,
})
</script>

View File

@ -1,157 +0,0 @@
<template>
<div class="my-flex-column w100 h100">
<el-card class="my-query-box mt8" shadow="never">
<el-form :model="state.filterModel" :inline="true" @submit.stop.prevent>
<el-form-item prop="name">
<el-input v-model="state.filterModel.name" placeholder="名称或编码" @keyup.enter="onQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="ele-Search" @click="onQuery"> 查询 </el-button>
<el-button v-auth="'api:admin:dict:add'" type="primary" icon="ele-Plus" @click="onAdd"> 新增 </el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="my-fill mt8" shadow="never">
<el-table
ref="tableRef"
v-loading="state.loading"
:data="state.dictTypeListData"
row-key="id"
highlight-current-row
style="width: 100%"
@current-change="onTableCurrentChange"
>
<el-table-column prop="name" label="名称" min-width="120" show-overflow-tooltip />
<el-table-column prop="code" label="编码" min-width="120" show-overflow-tooltip />
<el-table-column prop="sort" label="排序" width="60" align="center" show-overflow-tooltip />
<el-table-column label="状态" width="82" align="center">
<template #default="{ row }">
<el-tag type="success" v-if="row.enabled">启用</el-tag>
<el-tag type="danger" v-else>禁用</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="140" fixed="right" header-align="center" align="center">
<template #default="{ row }">
<el-button v-auth="'api:admin:dict:update'" icon="ele-EditPen" text type="primary" @click="onEdit(row)">编辑</el-button>
<el-button v-auth="'api:admin:dict:delete'" icon="ele-Delete" text type="danger" @click="onDelete(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="my-flex my-flex-end" style="margin-top: 20px">
<el-pagination
v-model:currentPage="state.pageInput.currentPage"
v-model:page-size="state.pageInput.pageSize"
:total="state.total"
:page-sizes="[10, 20, 50, 100]"
background
@size-change="onSizeChange"
@current-change="onCurrentChange"
layout="total, sizes, prev, pager, next, jumper"
/>
</div>
</el-card>
<dict-type-form ref="dictTypeFormRef" :title="state.dictTypeFormTitle"></dict-type-form>
</div>
</template>
<script lang="ts" setup name="admin/dictType">
import { ref, reactive, onMounted, getCurrentInstance, onBeforeMount, nextTick, defineAsyncComponent } from 'vue'
import { DictTypeGetPageOutput, PageInputDictTypeGetPageInput } from '/@/api/admin/data-contracts'
import { DictTypeApi } from '/@/api/admin/DictType'
import eventBus from '/@/utils/mitt'
//
const DictTypeForm = defineAsyncComponent(() => import('./components/dict-type-form.vue'))
const { proxy } = getCurrentInstance() as any
const tableRef = ref()
const dictTypeFormRef = ref()
const emits = defineEmits(['change'])
const state = reactive({
loading: false,
dictTypeFormTitle: '',
filterModel: {
name: '',
},
total: 0,
pageInput: {
currentPage: 1,
pageSize: 20,
} as PageInputDictTypeGetPageInput,
dictTypeListData: [] as Array<DictTypeGetPageOutput>,
lastCurrentRow: {} as DictTypeGetPageOutput,
})
onMounted(() => {
onQuery()
eventBus.off('refreshDictType')
eventBus.on('refreshDictType', () => {
onQuery()
})
})
onBeforeMount(() => {
eventBus.off('refreshDictType')
})
const onQuery = async () => {
state.loading = true
state.pageInput.filter = state.filterModel
const res = await new DictTypeApi().getPage(state.pageInput).catch(() => {
state.loading = false
})
state.dictTypeListData = res?.data?.list ?? []
state.total = res?.data?.total ?? 0
if (state.dictTypeListData.length > 0) {
nextTick(() => {
tableRef.value!.setCurrentRow(state.dictTypeListData[0])
})
}
state.loading = false
}
const onAdd = () => {
state.dictTypeFormTitle = '新增字典分类'
dictTypeFormRef.value.open()
}
const onEdit = (row: DictTypeGetPageOutput) => {
state.dictTypeFormTitle = '编辑字典分类'
dictTypeFormRef.value.open(row)
}
const onDelete = (row: DictTypeGetPageOutput) => {
proxy.$modal
.confirmDelete(`确定要删除【${row.name}】?`)
.then(async () => {
await new DictTypeApi().delete({ id: row.id }, { loading: true, showSuccessMessage: true })
onQuery()
})
.catch(() => {})
}
const onSizeChange = (val: number) => {
state.pageInput.currentPage = 1
state.pageInput.pageSize = val
onQuery()
}
const onCurrentChange = (val: number) => {
state.pageInput.currentPage = val
onQuery()
}
const onTableCurrentChange = (currentRow: DictTypeGetPageOutput) => {
if (state.lastCurrentRow?.id != currentRow?.id) {
state.lastCurrentRow = currentRow
emits('change', currentRow)
}
}
</script>
<style scoped lang="scss"></style>

View File

@ -1,237 +0,0 @@
<template>
<div class="my-flex-column w100 h100">
<el-card class="my-query-box mt8" shadow="never">
<el-form :model="state.filterModel" :inline="true" @submit.stop.prevent>
<el-form-item prop="name">
<el-input v-model="state.filterModel.name" placeholder="名称或编码" @keyup.enter="onQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="ele-Search" @click="onQuery"> 查询 </el-button>
<el-button v-auth="'api:admin:dict:add'" type="primary" icon="ele-Plus" @click="onAdd"> 新增 </el-button>
<el-button v-auth="'api:admin:dict:import-data'" icon="ele-Download" type="primary" @click="onImport"> 导入 </el-button>
<el-button v-auth="'api:admin:dict:export-data'" icon="ele-Upload" type="primary" :loading="state.export.loading" @click="onExport">
导出
</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="my-fill mt8" shadow="never">
<el-table
v-loading="state.loading"
:data="state.dictListData"
row-key="id"
style="width: 100%"
:default-sort="state.defalutSort"
@sort-change="onSortChange"
border
>
<el-table-column prop="name" label="名称" min-width="120" show-overflow-tooltip />
<el-table-column prop="code" label="编码" min-width="120" show-overflow-tooltip />
<el-table-column prop="value" label="值" width="90" sortable="custom" show-overflow-tooltip />
<el-table-column prop="sort" label="排序" width="90" align="center" sortable="custom" show-overflow-tooltip />
<el-table-column label="状态" width="82" align="center">
<template #default="{ row }">
<el-tag type="success" v-if="row.enabled">启用</el-tag>
<el-tag type="danger" v-else>禁用</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="140" fixed="right" header-align="center" align="center">
<template #default="{ row }">
<el-button v-auth="'api:admin:dict:update'" icon="ele-EditPen" text type="primary" @click="onEdit(row)">编辑</el-button>
<el-button v-auth="'api:admin:dict:delete'" icon="ele-Delete" text type="danger" @click="onDelete(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="my-flex my-flex-end" style="margin-top: 10px">
<el-pagination
v-model:currentPage="state.pageInput.currentPage"
v-model:page-size="state.pageInput.pageSize"
:total="state.total"
:page-sizes="[10, 20, 50, 100]"
background
@size-change="onSizeChange"
@current-change="onCurrentChange"
layout="total, sizes, prev, pager, next, jumper"
/>
</div>
</el-card>
<dict-form ref="dictFormRef" :title="state.dictFormTitle"></dict-form>
<MyImport ref="dictImportRef" :title="state.import.title" v-model="state.import"></MyImport>
</div>
</template>
<script lang="ts" setup name="admin/dictData">
import { ref, reactive, onMounted, getCurrentInstance, onBeforeMount, defineAsyncComponent } from 'vue'
import { DictGetPageOutput, PageInputDictGetPageInput, DictTypeGetPageOutput, SortInput } from '/@/api/admin/data-contracts'
import { DictApi } from '/@/api/admin/Dict'
import eventBus from '/@/utils/mitt'
import dayjs from 'dayjs'
import { RequestParams } from '/@/api/admin/http-client'
//
const DictForm = defineAsyncComponent(() => import('./components/dict-form.vue'))
const MyImport = defineAsyncComponent(() => import('/@/components/my-import/index.vue'))
const { proxy } = getCurrentInstance() as any
const dictFormRef = ref()
const dictImportRef = ref()
const defalutSort = { prop: 'sort', order: 'ascending' }
const getSortList = (data: { prop: string; order: any }) => {
return [
{
propName: data.prop,
order: data.order === 'ascending' ? 0 : data.order === 'descending' ? 1 : undefined,
},
] as [SortInput]
}
const state = reactive({
loading: false,
dictFormTitle: '',
filterModel: {
name: '',
dictTypeId: 0,
},
defalutSort: defalutSort,
total: 0,
pageInput: {
currentPage: 1,
pageSize: 20,
sortList: getSortList(defalutSort),
} as PageInputDictGetPageInput,
dictListData: [] as Array<DictGetPageOutput>,
dictTypeName: '',
import: {
title: '',
action: import.meta.env.VITE_API_URL + '/api/admin/dict/import-data',
downloadTemplate: (params: RequestParams) => new DictApi().downloadTemplate(params),
downloadErrorMark: (query: any, params: RequestParams) => new DictApi().downloadErrorMark(query, params),
duplicateAction: 1,
uniqueRules: ['字典名称', '字典编码', '字典值'],
requiredColumns: ['字典类型', '字典名称'],
},
export: {
loading: false,
},
})
onMounted(async () => {
eventBus.off('refreshDict')
eventBus.on('refreshDict', () => {
onQuery()
})
})
onBeforeMount(() => {
eventBus.off('refreshDict')
})
const onSortChange = (data: { column: any; prop: string; order: any }) => {
state.pageInput.sortList = getSortList(data)
onQuery()
}
const onQuery = async () => {
state.loading = true
state.pageInput.filter = state.filterModel
const res = await new DictApi().getPage(state.pageInput).catch(() => {
state.loading = false
})
state.dictListData = res?.data?.list ?? []
state.total = res?.data?.total ?? 0
state.loading = false
}
const onAdd = () => {
if (!(state.filterModel.dictTypeId > 0)) {
proxy.$modal.msgWarning('请选择字典类型')
return
}
state.dictFormTitle = `新增【${state.dictTypeName}】字典数据`
dictFormRef.value.open({ dictTypeId: state.filterModel.dictTypeId })
}
const onEdit = (row: DictGetPageOutput) => {
state.dictFormTitle = `编辑【${state.dictTypeName}】字典数据`
dictFormRef.value.open(row)
}
const onDelete = (row: DictGetPageOutput) => {
proxy.$modal
.confirmDelete(`确定要删除【${row.name}】?`)
.then(async () => {
await new DictApi().delete({ id: row.id }, { loading: true, showSuccessMessage: true })
onQuery()
})
.catch(() => {})
}
const onImport = () => {
state.import.title = `导入【${state.dictTypeName}】字典数据`
dictImportRef.value.open()
}
const onExport = async () => {
state.export.loading = true
await new DictApi()
.exportData(
{
dynamicFilter: {
filters: [{ field: 'dictTypeId', operator: 6, value: state.pageInput.filter?.dictTypeId }],
},
sortList: state.pageInput.sortList,
},
{ format: 'blob', returnResponse: true }
)
.then((res: any) => {
const contentDisposition = res.headers['content-disposition']
const matchs = /filename="?([^;"]+)/i.exec(contentDisposition)
let fileName = ''
if (matchs && matchs.length > 1) {
fileName = decodeURIComponent(matchs[1])
} else {
fileName = `数据字典列表${dayjs().format('YYYYMMDDHHmmss')}.xlsx`
}
const a = document.createElement('a')
a.download = fileName
a.href = URL.createObjectURL(res.data as Blob)
a.click()
URL.revokeObjectURL(a.href)
})
.finally(() => {
state.export.loading = false
})
}
const onSizeChange = (val: number) => {
state.pageInput.currentPage = 1
state.pageInput.pageSize = val
onQuery()
}
const onCurrentChange = (val: number) => {
state.pageInput.currentPage = val
onQuery()
}
const refresh = (data: DictTypeGetPageOutput) => {
if ((data?.id as number) > 0) {
state.filterModel.dictTypeId = data.id as number
state.dictTypeName = data.name as string
onQuery()
}
}
defineExpose({
refresh,
})
</script>
<style scoped lang="scss"></style>

View File

@ -1,29 +0,0 @@
<template>
<MySplitPanes>
<pane size="50" min-size="30" max-size="70">
<dict-type @change="onChange"></dict-type>
</pane>
<pane>
<dict ref="dictRef"></dict>
</pane>
</MySplitPanes>
</template>
<script lang="ts" setup name="admin/dict">
import { defineAsyncComponent, ref } from 'vue'
import { Pane } from 'splitpanes'
import { DictTypeGetPageOutput } from '/@/api/admin/data-contracts'
//
const DictType = defineAsyncComponent(() => import('./dict-type.vue'))
const Dict = defineAsyncComponent(() => import('./dict.vue'))
const MySplitPanes = defineAsyncComponent(() => import('/@/components/my-layout/split-panes.vue'))
const dictRef = ref()
const onChange = (data: DictTypeGetPageOutput) => {
dictRef.value?.refresh(data)
}
</script>
<style scoped lang="scss"></style>

View File

@ -1,143 +0,0 @@
<template>
<div>
<el-dialog v-model="state.showDialog" :title="title" draggable :close-on-click-modal="false" :close-on-press-escape="false" width="600px">
<div class="mb15">
<el-row :gutter="35">
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-input v-model="state.fileDirectory" placeholder="文件目录" clearable />
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-switch v-model="state.fileReName" active-text="文件自动重命名" />
</el-col>
</el-row>
<div class="mt5">
<el-alert class="my-el-alert" title="文件目录不填则默认使用本地上传格式upload/yyyy/MM/dd" type="info" :closable="false" />
</div>
</div>
<div>
<!-- :before-remove="() => false" -->
<el-upload
ref="uploadRef"
:action="uploadAction"
:headers="uploadHeaders"
:data="{ fileDirectory: state.fileDirectory, fileReName: state.fileReName }"
drag
multiple
:auto-upload="false"
:before-upload="
() => {
state.token = storesUserInfo.getToken()
}
"
:on-success="onSuccess"
:on-error="onError"
>
<el-icon class="el-icon--upload"><ele-UploadFilled /></el-icon>
<div class="el-upload__text">拖拽上传或<em>点击上传</em></div>
<!-- <template #tip>
<div class="el-upload__tip"></div>
</template> -->
</el-upload>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="onClear">清空已上传</el-button>
<el-button @click="onCancel"> </el-button>
<el-button type="primary" @click="onSure" :loading="state.sureLoading"> </el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup name="admin/file/upload">
import { ref, reactive, computed } from 'vue'
import { ElMessage } from 'element-plus'
import type { UploadInstance, UploadProps, UploadFile } from 'element-plus'
import eventBus from '/@/utils/mitt'
import { useUserInfo } from '/@/stores/userInfo'
const storesUserInfo = useUserInfo()
const uploadRef = ref<UploadInstance>()
defineProps({
title: {
type: String,
default: '',
},
})
const state = reactive({
showDialog: false,
sureLoading: false,
fileDirectory: '',
fileReName: true,
fileList: [] as UploadFile[],
token: storesUserInfo.getToken(),
})
const uploadAction = computed(() => {
return import.meta.env.VITE_API_URL + '/api/admin/file/upload-file'
})
const uploadHeaders = computed(() => {
return { Authorization: 'Bearer ' + state.token }
})
//
const open = async () => {
state.showDialog = true
}
//
const onError: UploadProps['onError'] = (error) => {
let message = ''
if (error.message) {
try {
message = JSON.parse(error.message)?.msg
} catch (err) {
message = error.message || ''
}
}
if (message)
ElMessage({
message: message,
type: 'error',
})
}
//
const onSuccess: UploadProps['onSuccess'] = (response) => {
if (response?.success) {
eventBus.emit('refreshFile')
}
}
//
const onClear = async () => {
uploadRef.value!.clearFiles(['success', 'fail'])
}
//
const onCancel = () => {
state.showDialog = false
}
//
const onSure = async () => {
uploadRef.value!.submit()
}
defineExpose({
open,
})
</script>
<style scoped lang="scss">
.my-el-alert {
border: none;
padding-left: 5px;
padding-right: 5px;
}
</style>

View File

@ -1,190 +0,0 @@
<template>
<my-layout>
<el-card class="my-query-box mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
<el-form :model="state.filterModel" :inline="true" @submit.stop.prevent>
<el-form-item prop="name">
<el-input v-model="state.filterModel.fileName" placeholder="文件名" @keyup.enter="onQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="ele-Search" @click="onQuery"> 查询 </el-button>
<el-button v-auth="'api:admin:file:upload-file'" type="primary" icon="ele-Upload" @click="onUpload"> 上传 </el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="my-fill mt8" shadow="never">
<el-table v-loading="state.loading" :data="state.fileListData" row-key="id" style="width: 100%" border>
<el-table-column prop="fileName" label="文件名" min-width="220">
<template #default="{ row }">
<div class="my-flex">
<el-image
v-if="isImage(row.extension)"
:src="row.linkUrl"
:preview-src-list="previewImglist"
:initial-index="getInitialIndex(row.linkUrl)"
:lazy="true"
:hide-on-click-modal="true"
fit="scale-down"
preview-teleported
style="width: 80px; height: 80px"
/>
<div class="ml10 my-flex-fill my-flex-y-center">
<div>{{ (row.fileName || '') + (row.extension || '') }}</div>
</div>
</div>
</template>
</el-table-column>
<el-table-column prop="sizeFormat" label="大小" width="120" />
<el-table-column prop="createdUserName" label="上传者" width="82">
<template #default="{ row }">
{{ row.modifiedUserName || row.createdUserName || '' }}
</template>
</el-table-column>
<el-table-column prop="createdTime" label="更新时间" width="100">
<template #default="{ row }">
{{ formatterTime(row.modifiedTime || row.createdTime || '') }}
</template>
</el-table-column>
<el-table-column prop="providerName" label="供应商" width="82" />
<el-table-column prop="bucketName" label="存储桶" min-width="120" />
<el-table-column prop="fileDirectory" label="目录" min-width="120" />
<el-table-column label="操作" width="180" fixed="right" header-align="center" align="center">
<template #default="{ row }">
<el-popover :width="220">
<p>{{ row.linkUrl }}</p>
<div class="mt10" style="text-align: right; margin: 0">
<el-button icon="ele-CopyDocument" type="primary" @click="copyText(row.linkUrl)">复制地址</el-button>
</div>
<template #reference>
<el-button text type="primary">地址</el-button>
</template>
</el-popover>
<el-link class="my-el-link mr12 ml12" :href="row.linkUrl" type="primary" icon="ele-Download" underline="never" target="_blank"
>下载</el-link
>
<el-button v-auth="'api:admin:file:delete'" icon="ele-Delete" text type="danger" @click="onDelete(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="my-flex my-flex-end" style="margin-top: 10px">
<el-pagination
v-model:currentPage="state.pageInput.currentPage"
v-model:page-size="state.pageInput.pageSize"
:total="state.total"
:page-sizes="[10, 20, 50, 100]"
background
@size-change="onSizeChange"
@current-change="onCurrentChange"
layout="total, sizes, prev, pager, next, jumper"
/>
</div>
</el-card>
<file-upload ref="fileUploadRef" title="上传文件"></file-upload>
</my-layout>
</template>
<script lang="ts" setup name="admin/file">
import { ref, reactive, onMounted, onBeforeMount, defineAsyncComponent, computed, getCurrentInstance } from 'vue'
import { PageInputFileGetPageInput, FileGetPageOutput } from '/@/api/admin/data-contracts'
import { FileApi } from '/@/api/admin/File'
import dayjs from 'dayjs'
import eventBus from '/@/utils/mitt'
import { isImage } from '/@/utils/test'
import commonFunction from '/@/utils/commonFunction'
const { proxy } = getCurrentInstance() as any
const FileUpload = defineAsyncComponent(() => import('./components/file-upload.vue'))
const fileUploadRef = ref()
const state = reactive({
loading: false,
fileFormTitle: '',
filterModel: {
fileName: '',
},
total: 0,
pageInput: {
currentPage: 1,
pageSize: 20,
} as PageInputFileGetPageInput,
fileListData: [] as Array<FileGetPageOutput>,
fileLogsTitle: '',
})
const { copyText } = commonFunction()
const previewImglist = computed(() => {
let imgList = [] as string[]
state.fileListData.forEach((a) => {
if (isImage(a.extension as string) && a.linkUrl) {
imgList.push(a.linkUrl as string)
}
})
return imgList
})
onMounted(() => {
onQuery()
eventBus.off('refreshFile')
eventBus.on('refreshFile', async () => {
onQuery()
})
})
onBeforeMount(() => {
eventBus.off('refreshFile')
})
const formatterTime = (cellValue: any) => {
return dayjs(cellValue).format('YYYY-MM-DD HH:mm:ss')
}
const getInitialIndex = (imgUrl: string) => {
return previewImglist.value.indexOf(imgUrl)
}
const onQuery = async () => {
state.loading = true
const res = await new FileApi().getPage({ ...state.pageInput, filter: state.filterModel }).catch(() => {
state.loading = false
})
state.fileListData = res?.data?.list ?? []
state.total = res?.data?.total ?? 0
state.loading = false
}
const onSizeChange = (val: number) => {
state.pageInput.currentPage = 1
state.pageInput.pageSize = val
onQuery()
}
const onCurrentChange = (val: number) => {
state.pageInput.currentPage = val
onQuery()
}
const onUpload = () => {
fileUploadRef.value.open()
}
const onDelete = (row: FileGetPageOutput) => {
proxy.$modal
.confirmDelete(`确定要删除文件【${row.fileName}${row.extension}】?`)
.then(async () => {
await new FileApi().delete({ id: row.id as number }, { loading: true, showSuccessMessage: true })
onQuery()
})
.catch(() => {})
}
</script>
<style scoped lang="scss">
.my-el-link {
font-size: 12px;
}
</style>

View File

@ -1,101 +0,0 @@
<template>
<el-drawer
v-model="state.showDialog"
title="操作日志详细信息"
direction="rtl"
destroy-on-close
size="600"
:append-to-body="true"
:lock-scroll="false"
>
<div class="my-fill h100" style="padding: 12px">
<el-descriptions class="margin-top" :column="1" border>
<el-descriptions-item label="操作名称" label-class-name="label">{{ state.details.apiLabel }}</el-descriptions-item>
<el-descriptions-item label="操作接口" label-class-name="label">{{ state.details.apiPath }}</el-descriptions-item>
<el-descriptions-item label="操作状态" label-class-name="label"
><el-tag :type="state.details.status ? 'success' : 'danger'" disable-transitions>{{
state.details.status ? '成功' : '失败'
}}</el-tag></el-descriptions-item
>
<el-descriptions-item label="请求方法" label-class-name="label">{{ state.details.apiMethod }}</el-descriptions-item>
<el-descriptions-item label="响应代码 " label-class-name="label">{{ state.details.statusCode }}</el-descriptions-item>
<el-descriptions-item label="IP地址" label-class-name="label">{{ state.details.ip }} {{ state.details.isp }}</el-descriptions-item>
<el-descriptions-item label="IP所在地" label-class-name="label"
>{{ state.details.country }} {{ state.details.province }} {{ state.details.city }}
</el-descriptions-item>
<el-descriptions-item label="浏览器信息" label-class-name="label"
>{{ state.details.os }} {{ state.details.browser }} {{ state.details.device }}
</el-descriptions-item>
<el-descriptions-item label="耗时ms" label-class-name="label">{{ state.details.elapsedMilliseconds }}</el-descriptions-item>
<el-descriptions-item label="操作账号" label-class-name="label">{{ state.details.createdUserName }}</el-descriptions-item>
<el-descriptions-item label="操作人员" label-class-name="label">{{ state.details.createdUserRealName }}</el-descriptions-item>
<el-descriptions-item label="创建时间" label-class-name="label">{{
dayjs(state.details.createdTime).format('YYYY-MM-DD HH:mm:ss')
}}</el-descriptions-item>
</el-descriptions>
<el-collapse class="mt12" v-model="state.activeName">
<el-collapse-item title="请求参数" name="params">
<MyJsonEditor
ref="jsonEditorRef"
v-model="state.details.params"
:options="{
mainMenuBar: false,
statusBar: false,
onEditable: () => false,
}"
style="height: 400px !important"
></MyJsonEditor>
</el-collapse-item>
<el-collapse-item title="响应结果" name="result">
<MyJsonEditor
ref="jsonEditorRef"
v-model="state.details.result"
:options="{
mainMenuBar: false,
statusBar: false,
onEditable: () => false,
}"
style="height: 200px !important"
></MyJsonEditor>
</el-collapse-item>
</el-collapse>
</div>
</el-drawer>
</template>
<script lang="ts" setup name="operation-log-details">
import { reactive } from 'vue'
import { OperationLogGetPageOutput } from '/@/api/admin/data-contracts'
import dayjs from 'dayjs'
import MyJsonEditor from '/@/components/my-json-editor/index.vue'
const state = reactive({
showDialog: false,
details: {} as OperationLogGetPageOutput,
activeName: ['params', 'result'],
})
//
const open = (row: OperationLogGetPageOutput) => {
state.showDialog = true
state.details = row
}
//
const onCancel = () => {
state.showDialog = false
}
defineExpose({
open,
})
</script>
<style scoped lang="scss">
:deep() {
.el-descriptions .label {
width: 120px;
}
}
</style>

View File

@ -1,137 +0,0 @@
<template>
<my-layout>
<el-card class="my-query-box mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
<el-form ref="filterFormRef" :model="state.filter" :inline="true" label-width="auto" :label-position="'left'" @submit.stop.prevent>
<el-form-item label="登录账号" prop="createdUserName">
<el-input v-model="state.filter.createdUserName" placeholder="登录账号" @keyup.enter="onQuery" />
</el-form-item>
<el-form-item label="登录状态" prop="status">
<el-select v-model="state.filter.status" :empty-values="[null]" style="width: 120px" @change="onQuery">
<el-option v-for="status in state.statusList" :key="status.name" :label="status.name" :value="status.value" />
</el-select>
</el-form-item>
<el-form-item label="登录IP" prop="ip">
<el-input v-model="state.filter.ip" placeholder="登录IP" @keyup.enter="onQuery" />
</el-form-item>
<el-form-item label="登录时间">
<MyDateRange
v-model:startDate="state.filter.addStartTime as string"
v-model:endDate="state.filter.addEndTime as string"
:shortcuts="[]"
style="width: 230px"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="ele-Search" @click="onQuery"> 查询 </el-button>
<el-button icon="ele-RefreshLeft" text bg @click="onReset(filterFormRef)"> 重置 </el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="my-fill mt8" shadow="never">
<el-table v-loading="state.loading" :data="state.loginLogListData" row-key="id" style="width: 100%" border>
<el-table-column prop="createdUserName" label="登录账号" min-width="150" show-overflow-tooltip>
<template #default="{ row }">
<el-badge :type="row.status ? 'success' : 'danger'" is-dot :offset="[0, 12]"></el-badge>
{{ row.createdUserName }}<br />{{ row.nickName }}
</template>
</el-table-column>
<el-table-column prop="ip" label="登录IP" min-width="150">
<template #default="{ row }"> {{ row.ip }} {{ row.isp }} </template>
</el-table-column>
<el-table-column prop="country" label="登录地区" min-width="150" show-overflow-tooltip>
<template #default="{ row }"> {{ row.country }} {{ row.province }} {{ row.city }} </template>
</el-table-column>
<el-table-column prop="os" label="操作系统" min-width="120" show-overflow-tooltip />
<el-table-column prop="browser" label="浏览器" min-width="120" show-overflow-tooltip />
<el-table-column prop="elapsedMilliseconds" label="耗时 ms" min-width="120" />
<el-table-column prop="msg" label="登录信息" min-width="150" show-overflow-tooltip />
<el-table-column prop="createdTime" label="登录时间" :formatter="formatterTime" min-width="160" />
</el-table>
<div class="my-flex my-flex-end" style="margin-top: 10px">
<el-pagination
v-model:currentPage="state.pageInput.currentPage"
v-model:page-size="state.pageInput.pageSize"
:total="state.total"
:page-sizes="[10, 20, 50, 100]"
background
@size-change="onSizeChange"
@current-change="onCurrentChange"
layout="total, sizes, prev, pager, next, jumper"
/>
</div>
</el-card>
</my-layout>
</template>
<script lang="ts" setup name="admin/loginLog">
import { reactive, onMounted, ref, defineAsyncComponent } from 'vue'
import { PageInputLoginLogGetPageInput, LoginLogGetPageInput, LoginLogGetPageOutput } from '/@/api/admin/data-contracts'
import { LoginLogApi } from '/@/api/admin/LoginLog'
import dayjs from 'dayjs'
import type { FormInstance } from 'element-plus'
const MyDateRange = defineAsyncComponent(() => import('/@/components/my-date-range/index.vue'))
const filterFormRef = ref<FormInstance>()
const state = reactive({
loading: false,
loginLogFormTitle: '',
filter: {} as LoginLogGetPageInput,
total: 0,
pageInput: {
currentPage: 1,
pageSize: 20,
} as PageInputLoginLogGetPageInput,
loginLogListData: [] as Array<LoginLogGetPageOutput>,
loginLogLogsTitle: '',
statusList: [
{ name: '全部', value: undefined },
{ name: '成功', value: true },
{ name: '失败', value: false },
],
})
onMounted(() => {
onQuery()
})
const formatterTime = (row: any, column: any, cellValue: any) => {
return dayjs(cellValue).format('YYYY-MM-DD HH:mm:ss')
}
const onQuery = async () => {
state.loading = true
state.pageInput.filter = state.filter
const res = await new LoginLogApi().getPage(state.pageInput).catch(() => {
state.loading = false
})
state.loginLogListData = res?.data?.list ?? []
state.total = res?.data?.total ?? 0
state.loading = false
}
const onReset = (formEl: FormInstance | undefined) => {
if (!formEl) return
state.filter.addStartTime = undefined
state.filter.addEndTime = undefined
formEl.resetFields()
onQuery()
}
const onSizeChange = (val: number) => {
state.pageInput.currentPage = 1
state.pageInput.pageSize = val
onQuery()
}
const onCurrentChange = (val: number) => {
state.pageInput.currentPage = val
onQuery()
}
</script>
<style scoped lang="scss"></style>

View File

@ -1,150 +0,0 @@
<template>
<my-layout>
<el-card class="my-query-box mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
<el-form ref="filterFormRef" :model="state.filter" :inline="true" label-width="auto" :label-position="'left'" @submit.stop.prevent>
<el-form-item label="操作账号" prop="createdUserName">
<el-input v-model="state.filter.createdUserName" placeholder="操作账号" @keyup.enter="onQuery" />
</el-form-item>
<el-form-item label="操作状态" prop="status">
<el-select v-model="state.filter.status" :empty-values="[null]" style="width: 120px" @change="onQuery">
<el-option v-for="status in state.statusList" :key="status.name" :label="status.name" :value="status.value" />
</el-select>
</el-form-item>
<el-form-item label="操作接口" prop="api">
<el-input v-model="state.filter.api" placeholder="操作接口" @keyup.enter="onQuery" />
</el-form-item>
<el-form-item label="操作IP" prop="ip">
<el-input v-model="state.filter.ip" placeholder="操作IP" @keyup.enter="onQuery" />
</el-form-item>
<el-form-item label="操作时间">
<MyDateRange v-model:startDate="state.filter.addStartTime" v-model:endDate="state.filter.addEndTime" :shortcuts="[]" style="width: 230px" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="ele-Search" @click="onQuery"> 查询 </el-button>
<el-button icon="ele-RefreshLeft" text bg @click="onReset(filterFormRef)"> 重置 </el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="my-fill mt8" shadow="never">
<el-table ref="tableRef" v-loading="state.loading" :data="state.operationLogListData" row-key="id" style="width: 100%" border>
<el-table-column prop="createdUserName" label="操作账号" min-width="150" show-overflow-tooltip>
<template #default="{ row }">
<el-badge :type="row.status ? 'success' : 'danger'" is-dot :offset="[0, 12]"></el-badge>
{{ row.createdUserName }}<br />{{ row.nickName }}
</template>
</el-table-column>
<el-table-column prop="apiLabel" label="操作名称" min-width="220" show-overflow-tooltip />
<el-table-column prop="apiPath" label="操作接口" min-width="260" show-overflow-tooltip />
<el-table-column prop="ip" label="IP地址" min-width="150">
<template #default="{ row }"> {{ row.ip }} {{ row.isp }} </template>
</el-table-column>
<el-table-column prop="country" label="IP所在地" min-width="150" show-overflow-tooltip>
<template #default="{ row }"> {{ row.country }} {{ row.province }} {{ row.city }} </template>
</el-table-column>
<el-table-column prop="elapsedMilliseconds" label="耗时 ms" min-width="100" />
<el-table-column prop="createdTime" label="操作时间" :formatter="formatterTime" min-width="160" />
<el-table-column label="操作" width="100" fixed="right" header-align="center" align="center">
<template #default="{ row }">
<el-button text type="primary" @click="onShowDetails(row)">查看详情</el-button>
</template>
</el-table-column>
</el-table>
<div class="my-flex my-flex-end" style="margin-top: 10px">
<el-pagination
v-model:currentPage="state.pageInput.currentPage"
v-model:page-size="state.pageInput.pageSize"
:total="state.total"
:page-sizes="[10, 20, 50, 100]"
background
@size-change="onSizeChange"
@current-change="onCurrentChange"
layout="total, sizes, prev, pager, next, jumper"
/>
</div>
</el-card>
<Details ref="detailsRef"></Details>
</my-layout>
</template>
<script lang="ts" setup name="admin/operation-log">
import { reactive, onMounted, ref, defineAsyncComponent } from 'vue'
import { OperationLogGetPageOutput, PageInputOperationLogGetPageInput, OperationLogGetPageInput } from '/@/api/admin/data-contracts'
import { OperationLogApi } from '/@/api/admin/OperationLog'
import dayjs from 'dayjs'
import type { FormInstance, TableInstance } from 'element-plus'
const filterFormRef = ref<FormInstance>()
const tableRef = ref<TableInstance>()
const detailsRef = ref()
const MyDateRange = defineAsyncComponent(() => import('/@/components/my-date-range/index.vue'))
const Details = defineAsyncComponent(() => import('./components/details.vue'))
const state = reactive({
loading: false,
oprationLogFormTitle: '',
filter: {} as OperationLogGetPageInput,
total: 0,
pageInput: {
currentPage: 1,
pageSize: 20,
} as PageInputOperationLogGetPageInput,
operationLogListData: [] as Array<OperationLogGetPageOutput>,
operationLogLogsTitle: '',
statusList: [
{ name: '全部', value: undefined },
{ name: '成功', value: true },
{ name: '失败', value: false },
],
details: {},
})
onMounted(() => {
onQuery()
})
const formatterTime = (row: OperationLogGetPageOutput, column: any, cellValue: any) => {
return dayjs(cellValue).format('YYYY-MM-DD HH:mm:ss')
}
const onShowDetails = (row: OperationLogGetPageOutput) => {
detailsRef.value!.open(row)
}
const onReset = (formEl: FormInstance | undefined) => {
if (!formEl) return
state.filter.addStartTime = undefined
state.filter.addEndTime = undefined
formEl.resetFields()
onQuery()
}
const onQuery = async () => {
state.loading = true
state.pageInput.filter = state.filter
const res = await new OperationLogApi().getPage(state.pageInput).catch(() => {
state.loading = false
})
state.operationLogListData = res?.data?.list ?? []
state.total = res?.data?.total ?? 0
state.loading = false
}
const onSizeChange = (val: number) => {
state.pageInput.currentPage = 1
state.pageInput.pageSize = val
onQuery()
}
const onCurrentChange = async (val: number) => {
state.pageInput.currentPage = val
await onQuery()
tableRef.value?.setScrollTop(0)
}
</script>
<style scoped lang="scss"></style>

View File

@ -1,137 +0,0 @@
<template>
<div>
<el-dialog
v-model="state.showDialog"
destroy-on-close
:title="title"
draggable
:close-on-click-modal="false"
:close-on-press-escape="false"
width="600px"
>
<el-form :model="form" ref="formRef" label-width="80px">
<el-row :gutter="35">
<el-col v-if="!(form.id > 0 && !(Number(form.parentId) > 0))" :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="上级分组" prop="parentId">
<el-tree-select
v-model="form.parentId"
:data="treeData"
node-key="id"
:props="{ label: 'name' }"
check-strictly
default-expand-all
render-after-expand
fit-input-width
clearable
class="w100"
/>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="名称" prop="name" :rules="[{ required: true, message: '请输入名称', trigger: ['blur', 'change'] }]">
<el-input v-model="form.name" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="编码" prop="code">
<el-input v-model="form.code" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="排序">
<el-input-number v-model="form.sort" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel"> </el-button>
<el-button type="primary" @click="onSure" :loading="state.sureLoading"> </el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup name="admin/msg-type/form">
import { reactive, toRefs, ref, PropType } from 'vue'
import { MsgTypeGetListOutput, MsgTypeUpdateInput } from '/@/api/admin/data-contracts'
import { MsgTypeApi } from '/@/api/admin/MsgType'
import { cloneDeep } from 'lodash-es'
import eventBus from '/@/utils/mitt'
defineProps({
title: {
type: String,
default: '',
},
treeData: {
type: Array as PropType<MsgTypeGetListOutput[]>,
default: () => [],
},
})
const formRef = ref()
const state = reactive({
showDialog: false,
sureLoading: false,
form: {} as MsgTypeUpdateInput,
})
const { form } = toRefs(state)
//
const open = async (row: MsgTypeUpdateInput = { id: 0 }) => {
let formData = cloneDeep(row) as MsgTypeUpdateInput
if (row.id > 0) {
const res = await new MsgTypeApi().get({ id: row.id }, { loading: true })
if (res?.success) {
formData = res.data as MsgTypeUpdateInput
formData.parentId = formData.parentId && formData.parentId > 0 ? formData.parentId : undefined
} else {
return
}
}
state.form = formData
state.showDialog = true
}
//
const onCancel = () => {
state.showDialog = false
}
//
const onSure = () => {
formRef.value.validate(async (valid: boolean) => {
if (!valid) return
state.sureLoading = true
let res = {} as any
state.form.parentId = state.form.parentId && state.form.parentId > 0 ? state.form.parentId : undefined
if (state.form.id != undefined && state.form.id > 0) {
res = await new MsgTypeApi().update(state.form, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
} else {
res = await new MsgTypeApi().add(state.form, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
}
state.sureLoading = false
if (res?.success) {
eventBus.emit('refreshMsgType')
state.showDialog = false
}
})
}
defineExpose({
open,
})
</script>

View File

@ -1,131 +0,0 @@
<template>
<MyLayout>
<el-card class="my-query-box mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
<el-form :inline="true" @submit.stop.prevent>
<el-form-item label="分类名称">
<el-input v-model="state.filter.msgTypeName" placeholder="分类名称" @keyup.enter="onQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="ele-Search" @click="onQuery"> 查询 </el-button>
<el-button v-auth="'api:admin:msg-type:add'" type="primary" icon="ele-Plus" @click="onAdd"> 新增 </el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="my-fill mt8" shadow="never">
<el-table
ref="msgTypeTableRef"
v-loading="state.loading"
:data="state.msgTypeTreeData"
row-key="id"
default-expand-all
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
style="width: 100%"
border
>
<el-table-column prop="name" label="分类名称" min-width="120" show-overflow-tooltip />
<el-table-column prop="code" label="分类编码" min-width="120" show-overflow-tooltip />
<el-table-column prop="sort" label="排序" width="82" align="center" show-overflow-tooltip />
<el-table-column label="操作" width="180" fixed="right" header-align="center" align="right">
<template #default="{ row }">
<el-button v-if="row.parentId === 0" v-auth="'api:admin:msg-type:add'" icon="ele-Plus" text type="primary" @click="onAdd(row)"
>添加</el-button
>
<el-button v-auth="'api:admin:msg-type:update'" icon="ele-EditPen" text type="primary" @click="onEdit(row)">编辑</el-button>
<el-button v-auth="'api:admin:msg-type:delete'" icon="ele-Delete" text type="danger" @click="onDelete(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
<msg-type-form ref="msgTypeFormRef" :title="state.msgTypeFormTitle" :tree-data="state.msgTypeFormTreeData"></msg-type-form>
</MyLayout>
</template>
<script lang="ts" setup name="admin/msg-type">
import { ref, reactive, onMounted, getCurrentInstance, onBeforeMount, nextTick, defineAsyncComponent } from 'vue'
import { MsgTypeGetListOutput } from '/@/api/admin/data-contracts'
import { MsgTypeApi } from '/@/api/admin/MsgType'
import { listToTree, filterTree } from '/@/utils/tree'
import { ElTable } from 'element-plus'
import { cloneDeep } from 'lodash-es'
import eventBus from '/@/utils/mitt'
//
const MsgTypeForm = defineAsyncComponent(() => import('./components/msg-type-form.vue'))
const { proxy } = getCurrentInstance() as any
const msgTypeTableRef = ref()
const msgTypeFormRef = ref()
const state = reactive({
loading: false,
userListLoading: false,
sureLoading: false,
msgTypeFormTitle: '',
filter: {
name: '',
msgTypeName: '',
},
msgTypeTreeData: [] as any,
msgTypeFormTreeData: [] as any,
msgTypeId: undefined as number | undefined,
msgTypeName: '' as string | null | undefined,
})
onMounted(() => {
onQuery()
eventBus.off('refreshMsgType')
eventBus.on('refreshMsgType', async () => {
onQuery()
})
})
onBeforeMount(() => {
eventBus.off('refreshMsgType')
})
const onQuery = async () => {
state.loading = true
const res = await new MsgTypeApi().getList().catch(() => {
state.loading = false
})
if (res && res.data && res.data.length > 0) {
state.msgTypeTreeData = filterTree(listToTree(cloneDeep(res.data)), state.filter.msgTypeName)
state.msgTypeFormTreeData = listToTree(cloneDeep(res.data).filter((a) => a.parentId === 0))
if (state.msgTypeTreeData.length > 0 && state.msgTypeTreeData[0].children?.length > 0) {
nextTick(() => {
msgTypeTableRef.value!.setCurrentRow(state.msgTypeTreeData[0].children[0])
})
}
} else {
state.msgTypeTreeData = []
state.msgTypeFormTreeData = []
}
state.loading = false
}
const onAdd = (row: MsgTypeGetListOutput | undefined = undefined) => {
state.msgTypeFormTitle = '新增消息分类'
msgTypeFormRef.value.open({ id: 0, parentId: row?.id })
}
const onEdit = (row: MsgTypeGetListOutput) => {
state.msgTypeFormTitle = '编辑消息分类'
msgTypeFormRef.value.open(row)
}
const onDelete = (row: MsgTypeGetListOutput) => {
proxy.$modal
.confirmDelete(`确定要删除消息分类【${row.name}】?`)
.then(async () => {
await new MsgTypeApi().delete({ id: row.id }, { loading: true })
onQuery()
})
.catch(() => {})
}
</script>
<style scoped lang="scss"></style>

View File

@ -1,253 +0,0 @@
<template>
<div>
<el-dialog
v-model="state.showDialog"
destroy-on-close
:title="title"
draggable
:close-on-click-modal="false"
:close-on-press-escape="false"
width="830px"
>
<el-form :model="form" ref="formRef" label-width="auto">
<el-row :gutter="35">
<!-- <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="分类" prop="typeId" :rules="[{ required: true, message: '请选择分类', trigger: ['change'] }]" hidden>
<el-tree-select
v-model="form.typeId"
placeholder="请选择分类"
:data="state.msgTypeTreeData"
node-key="id"
:props="{ label: 'name' }"
default-expand-all
fit-input-width
clearable
filterable
class="w100"
/>
</el-form-item>
</el-col> -->
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="标题" prop="title" :rules="[{ required: true, message: '请输入标题', trigger: ['blur', 'change'] }]">
<el-input v-model="form.title" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item
label="内容"
prop="content"
:rules="[
{ required: true, message: '请输入内容', trigger: ['blur', 'change'] },
{ validator: testEditorContent, trigger: ['blur', 'change'] },
]"
>
<MyEditor ref="editorRef" v-model:model-value="form.content" @onBlur="onValidateContent" @onChange="onValidateContent"></MyEditor>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
<el-form-item label="状态" prop="status" :rules="[{ required: true, message: '请选择状态', trigger: ['change'] }]">
<el-select v-model="form.status" placeholder="请选择状态" class="w100" @change="onStatusChange">
<el-option v-for="item in state.msgStatusList" :key="item.label" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" v-if="isScheduledStatus">
<el-form-item
label="发布时间"
prop="publishTime"
:rules="[{
required: isScheduledStatus,
message: '请选择发布时间',
trigger: ['change', 'blur']
}]"
>
<el-date-picker
v-model="form.publishTime"
type="datetime"
placeholder="请选择发布时间"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
:disabled-date="disabledDate"
class="w100"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel"> </el-button>
<el-button type="primary" @click="onSure" :loading="state.sureLoading"> </el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup name="admin/msg/form">
import { reactive, toRefs, ref, defineAsyncComponent, getCurrentInstance, computed } from 'vue'
import { MsgUpdateInput, MsgTypeGetListOutput } from '/@/api/admin/data-contracts'
import { MsgApi } from '/@/api/admin/Msg'
import { cloneDeep } from 'lodash-es'
import eventBus from '/@/utils/mitt'
import { MsgTypeApi } from '/@/api/admin/MsgType'
import { listToTree } from '/@/utils/tree'
import { MsgStatusEnum } from '/@/api/admin/enum-contracts'
import { toOptionsByValue } from '/@/utils/enum'
// MsgUpdateInput
interface ExtendedMsgUpdateInput extends MsgUpdateInput {
publishTime?: string
}
const MyEditor = defineAsyncComponent(() => import('/@/components/my-editor/index.vue'))
defineProps({
title: {
type: String,
default: '',
},
})
const formRef = ref()
const editorRef = ref()
const state = reactive({
showDialog: false,
sureLoading: false,
form: { content: '' } as ExtendedMsgUpdateInput,
msgTypeTreeData: [] as MsgTypeGetListOutput[],
v: null,
msgStatusList: toOptionsByValue(MsgStatusEnum),
})
const { proxy } = getCurrentInstance() as any
const { form } = toRefs(state)
//
const isScheduledStatus = computed(() => {
return state.form.status === 3 // 3 Scheduled
})
const testEditorContent = (rule: any, value: any, callback: any) => {
if (!value) {
callback()
}
if (editorRef.value.isEmpty()) {
callback(new Error('请输入内容'))
} else {
callback()
}
}
const onValidateContent = () => {
formRef.value.validateField('content')
}
//
const onStatusChange = (value: number) => {
if (value !== 3) { // 3
state.form.publishTime = undefined
//
formRef.value?.clearValidate('publishTime')
}
}
//
const disabledDate = (time: Date) => {
return time.getTime() < Date.now() - 86400000 //
}
const getMsgTypes = async () => {
const res = await new MsgTypeApi().getList().catch(() => {
state.msgTypeTreeData = []
})
if (res?.success && res.data && res.data.length > 0) {
state.msgTypeTreeData = listToTree(res.data)
} else {
state.msgTypeTreeData = []
}
}
//
const open = async (row: ExtendedMsgUpdateInput = { id: 0 }) => {
proxy.$modal.loading()
await getMsgTypes()
let formData = cloneDeep(row) as ExtendedMsgUpdateInput
if (row.id > 0) {
const res = await new MsgApi().get({ id: row.id }).catch(() => {
proxy.$modal.closeLoading()
})
if (res?.success) {
formData = res.data as ExtendedMsgUpdateInput
formData.typeId = formData.typeId && formData.typeId > 0 ? formData.typeId : undefined
}
}
state.form = formData
state.form.typeId = 690214253387845
proxy.$modal.closeLoading()
state.showDialog = true
}
//
const onCancel = () => {
state.showDialog = false
}
//
const onSure = () => {
formRef.value.validate(async (valid: boolean) => {
if (!valid) return
//
if (isScheduledStatus.value) {
if (!state.form.publishTime) {
proxy.$message.error('选择定时发布时必须选择发布时间')
return
}
const publishTime = new Date(state.form.publishTime)
const now = new Date()
if (publishTime <= now) {
proxy.$message.error('发布时间必须晚于当前时间')
return
}
} else {
//
state.form.publishTime = undefined
}
state.sureLoading = true
let res = {} as any
if (state.form.id != undefined && state.form.id > 0) {
res = await new MsgApi().update(state.form, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
} else {
res = await new MsgApi().add(state.form, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
}
state.sureLoading = false
if (res?.success) {
eventBus.emit('refreshMsg')
state.showDialog = false
}
})
}
defineExpose({
open,
})
</script>
<style lang="scss" scoped></style>

View File

@ -1,319 +0,0 @@
<template>
<MySplitPanes>
<pane size="55" min-size="35" max-size="65">
<div class="my-flex-column w100 h100">
<el-card class="my-query-box mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
<el-form :inline="true" @submit.stop.prevent>
<el-form-item label="标题">
<el-input v-model="state.filter.msgName" placeholder="标题" @keyup.enter="onQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="ele-Search" @click="onQuery"> 查询 </el-button>
<el-button v-auth="'api:admin:msg:add'" type="primary" icon="ele-Plus" @click="onAdd"> 新增 </el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="my-fill mt8" shadow="never">
<el-table
ref="msgTableRef"
v-loading="state.loading"
:data="state.msgData"
default-expand-all
highlight-current-row
style="width: 100%"
@current-change="onTableCurrentChange"
border
>
<el-table-column prop="title" label="标题" min-width="120" show-overflow-tooltip />
<el-table-column prop="typeName" label="消息分类" min-width="120" show-overflow-tooltip />
<el-table-column prop="status" label="状态" min-width="90" show-overflow-tooltip :formatter="formatterMsgStatusEnum" />
<el-table-column prop="createdTime" label="创建时间" :formatter="formatterTime" min-width="160" show-overflow-tooltip />
<el-table-column label="操作" width="100" fixed="right" header-align="center" align="center">
<template #default="{ row }">
<my-dropdown-more v-auths="['api:admin:msg:update', 'api:admin:msg:delete']" style="margin-left: 0px">
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item v-if="auth('api:admin:msg:update')" @click="onEdit(row)">编辑</el-dropdown-item>
<el-dropdown-item v-if="auth('api:admin:msg:delete')" @click="onDelete(row)">删除</el-dropdown-item>
</el-dropdown-menu>
</template>
</my-dropdown-more>
</template>
</el-table-column>
</el-table>
<div class="my-flex my-flex-end" style="margin-top: 10px">
<el-pagination
v-model:currentPage="state.pageInput.currentPage"
v-model:page-size="state.pageInput.pageSize"
:total="state.total"
:page-sizes="[10, 20, 50, 100]"
background
@size-change="onSizeChange"
@current-change="onCurrentChange"
layout="total, sizes, prev, pager, next, jumper"
/>
</div>
</el-card>
</div>
</pane>
<pane>
<div class="my-flex-column w100 h100">
<el-card class="my-query-box mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
<el-form :inline="true" @submit.stop.prevent>
<el-form-item label="姓名">
<el-input v-model="state.filter.name" placeholder="姓名" @keyup.enter="onGetMsgUserList" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="ele-Search" @click="onGetMsgUserList"> 查询 </el-button>
<el-button v-auth="'api:admin:msg:add-msg-user'" type="primary" icon="ele-Plus" @click="onAddUser"> 添加 </el-button>
<el-button v-auth="'api:admin:msg:remove-msg-user'" type="danger" icon="ele-Delete" @click="onRemoveUser"> 移除 </el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="my-fill mt8" shadow="never">
<el-table
ref="userTableRef"
v-loading="state.userListLoading"
:data="state.userListData"
row-key="id"
style="width: 100%"
@row-click="onUserRowClick"
border
>
<el-table-column type="selection" width="55" />
<el-table-column prop="name" label="姓名" min-width="120" show-overflow-tooltip />
<el-table-column label="是否已读" width="100" align="center">
<template #default="{ row }">
<el-tag type="success" v-if="row.isRead">已读</el-tag>
<el-tag type="info" v-else>未读</el-tag>
</template>
</el-table-column>
<el-table-column prop="readTime" label="已读时间" :formatter="formatterTime" min-width="160" show-overflow-tooltip />
<!-- <el-table-column prop="mobile" label="手机号" min-width="120" show-overflow-tooltip />
<el-table-column prop="email" label="邮箱" min-width="180" show-overflow-tooltip /> -->
</el-table>
</el-card>
</div>
</pane>
<msg-form ref="msgFormRef" :title="state.msgFormTitle"></msg-form>
<user-select
ref="userSelectRef"
:title="`添加【${state.msgName}】员工`"
multiple
:sure-loading="state.sureLoading"
@sure="onSureUser"
></user-select>
</MySplitPanes>
</template>
<script lang="ts" setup name="admin/msg">
import { ref, reactive, onMounted, getCurrentInstance, onBeforeMount, defineAsyncComponent } from 'vue'
import {
PageInputMsgGetPageInput,
MsgGetMsgUserListOutput,
UserGetPageOutput,
MsgAddMsgUserListInput,
MsgGetPageOutput,
} from '/@/api/admin/data-contracts'
import { MsgApi } from '/@/api/admin/Msg'
import { ElTable } from 'element-plus'
import eventBus from '/@/utils/mitt'
import { auth } from '/@/utils/authFunction'
import { Pane } from 'splitpanes'
import dayjs from 'dayjs'
import { MsgStatusEnum } from '/@/api/admin/enum-contracts'
import { getDescByValue } from '/@/utils/enum'
//
const MsgForm = defineAsyncComponent(() => import('./components/msg-form.vue'))
const UserSelect = defineAsyncComponent(() => import('/@/views/admin/user/components/user-select.vue'))
const MySplitPanes = defineAsyncComponent(() => import('/@/components/my-layout/split-panes.vue'))
const MyDropdownMore = defineAsyncComponent(() => import('/@/components/my-dropdown-more/index.vue'))
const { proxy } = getCurrentInstance() as any
const msgTableRef = ref()
const msgFormRef = ref()
const userTableRef = ref<InstanceType<typeof ElTable>>()
const userSelectRef = ref()
const state = reactive({
loading: false,
userListLoading: false,
sureLoading: false,
msgFormTitle: '',
filter: {
name: '',
msgName: '',
},
total: 0,
pageInput: {
currentPage: 1,
pageSize: 20,
filter: {
title: '',
msgTypeValue: 690214253387845,
},
} as PageInputMsgGetPageInput,
msgData: [] as any,
userListData: [] as MsgGetMsgUserListOutput[],
msgId: undefined as number | undefined,
msgName: '' as string | null | undefined,
})
onMounted(async () => {
onQuery()
eventBus.off('refreshMsg')
eventBus.on('refreshMsg', () => {
onQuery()
})
})
onBeforeMount(() => {
eventBus.off('refreshMsg')
})
const formatterMsgStatusEnum = (row: any, column: any, cellValue: any) => {
return getDescByValue(MsgStatusEnum, cellValue)
}
const formatterTime = (row: any, column: any, cellValue: any) => {
return cellValue ? dayjs(cellValue).format('YYYY-MM-DD HH:mm:ss') : ''
}
const onQuery = async () => {
state.loading = true
if (state.pageInput.filter) state.pageInput.filter.title = state.filter.msgName
const res = await new MsgApi().getPage(state.pageInput).catch(() => {
state.loading = false
})
state.msgData = res?.data?.list ?? []
state.total = res?.data?.total ?? 0
if (state.msgData?.length > 0) {
window.setTimeout(() => {
msgTableRef.value?.setCurrentRow(state.msgData[0])
}, 100)
// nextTick(() => {
// msgTableRef.value?.setCurrentRow(state.msgData[0])
// })
}
state.loading = false
}
const onSizeChange = (val: number) => {
state.pageInput.currentPage = 1
state.pageInput.pageSize = val
onQuery()
}
const onCurrentChange = (val: number) => {
state.pageInput.currentPage = val
onQuery()
}
const onAdd = () => {
state.msgFormTitle = '新增活动消息'
msgFormRef.value.open({ enabled: true })
}
const onEdit = (row: MsgGetPageOutput) => {
state.msgFormTitle = '编辑活动消息'
msgFormRef.value.open(row)
}
const onDelete = (row: MsgGetPageOutput) => {
proxy.$modal
.confirmDelete(`确定要删除消息【${row.title}】?`)
.then(async () => {
await new MsgApi().delete({ id: row.id }, { loading: true, showSuccessMessage: true })
onQuery()
})
.catch(() => {})
}
const onGetMsgUserList = async () => {
state.userListLoading = true
const res = await new MsgApi().getMsgUserList({ MsgId: state.msgId, Name: state.filter.name }).catch(() => {
state.userListLoading = false
})
state.userListLoading = false
if (res?.success) {
if (res.data && res.data.length > 0) {
state.userListData = res.data
} else {
state.userListData = []
}
}
}
const onTableCurrentChange = (currentRow: MsgGetPageOutput) => {
if (!currentRow) {
return
}
state.msgId = currentRow.id
state.msgName = currentRow.title
onGetMsgUserList()
}
const onUserRowClick = (row: MsgGetMsgUserListOutput) => {
userTableRef.value!.toggleRowSelection(row, undefined)
}
const onAddUser = () => {
if (!((state.msgId as number) > 0)) {
proxy.$modal.msgWarning('请选择消息')
return
}
userSelectRef.value.open({ msgId: state.msgId })
}
const onRemoveUser = () => {
if (!((state.msgId as number) > 0)) {
proxy.$modal.msgWarning('请选择消息')
return
}
const selectionRows = userTableRef.value!.getSelectionRows() as UserGetPageOutput[]
if (!((selectionRows.length as number) > 0)) {
proxy.$modal.msgWarning('请选择员工')
return
}
proxy.$modal
.confirm(`确定要移除吗?`)
.then(async () => {
const userIds = selectionRows?.map((a) => a.id)
const input = { msgId: state.msgId, userIds } as MsgAddMsgUserListInput
await new MsgApi().removeMsgUser(input, { loading: true })
onGetMsgUserList()
})
.catch(() => {})
}
const onSureUser = async (users: UserGetPageOutput[]) => {
if (!(users?.length > 0)) {
userSelectRef.value.close()
return
}
state.sureLoading = true
const userIds = users?.map((a) => a.id)
const input = { msgId: state.msgId, userIds } as MsgAddMsgUserListInput
await new MsgApi().addMsgUser(input, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
state.sureLoading = false
userSelectRef.value.close()
onGetMsgUserList()
}
</script>
<style scoped lang="scss"></style>

View File

@ -1,253 +0,0 @@
<template>
<div>
<el-dialog
v-model="state.showDialog"
destroy-on-close
:title="title"
draggable
:close-on-click-modal="false"
:close-on-press-escape="false"
width="830px"
>
<el-form :model="form" ref="formRef" label-width="auto">
<el-row :gutter="35">
<!-- <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="分类" prop="typeId" :rules="[{ required: true, message: '请选择分类', trigger: ['change'] }]" hidden>
<el-tree-select
v-model="form.typeId"
placeholder="请选择分类"
:data="state.msgTypeTreeData"
node-key="id"
:props="{ label: 'name' }"
default-expand-all
fit-input-width
clearable
filterable
class="w100"
/>
</el-form-item>
</el-col> -->
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="标题" prop="title" :rules="[{ required: true, message: '请输入标题', trigger: ['blur', 'change'] }]">
<el-input v-model="form.title" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item
label="内容"
prop="content"
:rules="[
{ required: true, message: '请输入内容', trigger: ['blur', 'change'] },
{ validator: testEditorContent, trigger: ['blur', 'change'] },
]"
>
<MyEditor ref="editorRef" v-model:model-value="form.content" @onBlur="onValidateContent" @onChange="onValidateContent"></MyEditor>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
<el-form-item label="状态" prop="status" :rules="[{ required: true, message: '请选择状态', trigger: ['change'] }]">
<el-select v-model="form.status" placeholder="请选择状态" class="w100" @change="onStatusChange">
<el-option v-for="item in state.msgStatusList" :key="item.label" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" v-if="isScheduledStatus">
<el-form-item
label="发布时间"
prop="publishTime"
:rules="[{
required: isScheduledStatus,
message: '请选择发布时间',
trigger: ['change', 'blur']
}]"
>
<el-date-picker
v-model="form.publishTime"
type="datetime"
placeholder="请选择发布时间"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
:disabled-date="disabledDate"
class="w100"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel"> </el-button>
<el-button type="primary" @click="onSure" :loading="state.sureLoading"> </el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup name="admin/msg/form">
import { reactive, toRefs, ref, defineAsyncComponent, getCurrentInstance, computed } from 'vue'
import { MsgUpdateInput, MsgTypeGetListOutput } from '/@/api/admin/data-contracts'
import { MsgApi } from '/@/api/admin/Msg'
import { cloneDeep } from 'lodash-es'
import eventBus from '/@/utils/mitt'
import { MsgTypeApi } from '/@/api/admin/MsgType'
import { listToTree } from '/@/utils/tree'
import { MsgStatusEnum } from '/@/api/admin/enum-contracts'
import { toOptionsByValue } from '/@/utils/enum'
// MsgUpdateInput
interface ExtendedMsgUpdateInput extends MsgUpdateInput {
publishTime?: string
}
const MyEditor = defineAsyncComponent(() => import('/@/components/my-editor/index.vue'))
defineProps({
title: {
type: String,
default: '',
},
})
const formRef = ref()
const editorRef = ref()
const state = reactive({
showDialog: false,
sureLoading: false,
form: { content: '' } as ExtendedMsgUpdateInput,
msgTypeTreeData: [] as MsgTypeGetListOutput[],
v: null,
msgStatusList: toOptionsByValue(MsgStatusEnum),
})
const { proxy } = getCurrentInstance() as any
const { form } = toRefs(state)
//
const isScheduledStatus = computed(() => {
return state.form.status === 3 // 3 Scheduled
})
const testEditorContent = (rule: any, value: any, callback: any) => {
if (!value) {
callback()
}
if (editorRef.value.isEmpty()) {
callback(new Error('请输入内容'))
} else {
callback()
}
}
const onValidateContent = () => {
formRef.value.validateField('content')
}
//
const onStatusChange = (value: number) => {
if (value !== 3) { // 3
state.form.publishTime = undefined
//
formRef.value?.clearValidate('publishTime')
}
}
//
const disabledDate = (time: Date) => {
return time.getTime() < Date.now() - 86400000 //
}
const getMsgTypes = async () => {
const res = await new MsgTypeApi().getList().catch(() => {
state.msgTypeTreeData = []
})
if (res?.success && res.data && res.data.length > 0) {
state.msgTypeTreeData = listToTree(res.data)
} else {
state.msgTypeTreeData = []
}
}
//
const open = async (row: ExtendedMsgUpdateInput = { id: 0 }) => {
proxy.$modal.loading()
await getMsgTypes()
let formData = cloneDeep(row) as ExtendedMsgUpdateInput
if (row.id > 0) {
const res = await new MsgApi().get({ id: row.id }).catch(() => {
proxy.$modal.closeLoading()
})
if (res?.success) {
formData = res.data as ExtendedMsgUpdateInput
formData.typeId = formData.typeId && formData.typeId > 0 ? formData.typeId : undefined
}
}
state.form = formData
state.form.typeId = 690213836861509
proxy.$modal.closeLoading()
state.showDialog = true
}
//
const onCancel = () => {
state.showDialog = false
}
//
const onSure = () => {
formRef.value.validate(async (valid: boolean) => {
if (!valid) return
//
if (isScheduledStatus.value) {
if (!state.form.publishTime) {
proxy.$message.error('选择定时发布时必须选择发布时间')
return
}
const publishTime = new Date(state.form.publishTime)
const now = new Date()
if (publishTime <= now) {
proxy.$message.error('发布时间必须晚于当前时间')
return
}
} else {
//
state.form.publishTime = undefined
}
state.sureLoading = true
let res = {} as any
if (state.form.id != undefined && state.form.id > 0) {
res = await new MsgApi().update(state.form, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
} else {
res = await new MsgApi().add(state.form, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
}
state.sureLoading = false
if (res?.success) {
eventBus.emit('refreshMsg')
state.showDialog = false
}
})
}
defineExpose({
open,
})
</script>
<style lang="scss" scoped></style>

View File

@ -1,319 +0,0 @@
<template>
<MySplitPanes>
<pane size="55" min-size="35" max-size="65">
<div class="my-flex-column w100 h100">
<el-card class="my-query-box mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
<el-form :inline="true" @submit.stop.prevent>
<el-form-item label="标题">
<el-input v-model="state.filter.msgName" placeholder="标题" @keyup.enter="onQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="ele-Search" @click="onQuery"> 查询 </el-button>
<el-button v-auth="'api:admin:msg:add'" type="primary" icon="ele-Plus" @click="onAdd"> 新增 </el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="my-fill mt8" shadow="never">
<el-table
ref="msgTableRef"
v-loading="state.loading"
:data="state.msgData"
default-expand-all
highlight-current-row
style="width: 100%"
@current-change="onTableCurrentChange"
border
>
<el-table-column prop="title" label="标题" min-width="120" show-overflow-tooltip />
<el-table-column prop="typeName" label="消息分类" min-width="120" show-overflow-tooltip />
<el-table-column prop="status" label="状态" min-width="90" show-overflow-tooltip :formatter="formatterMsgStatusEnum" />
<el-table-column prop="createdTime" label="创建时间" :formatter="formatterTime" min-width="160" show-overflow-tooltip />
<el-table-column label="操作" width="100" fixed="right" header-align="center" align="center">
<template #default="{ row }">
<my-dropdown-more v-auths="['api:admin:msg:update', 'api:admin:msg:delete']" style="margin-left: 0px">
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item v-if="auth('api:admin:msg:update')" @click="onEdit(row)">编辑</el-dropdown-item>
<el-dropdown-item v-if="auth('api:admin:msg:delete')" @click="onDelete(row)">删除</el-dropdown-item>
</el-dropdown-menu>
</template>
</my-dropdown-more>
</template>
</el-table-column>
</el-table>
<div class="my-flex my-flex-end" style="margin-top: 10px">
<el-pagination
v-model:currentPage="state.pageInput.currentPage"
v-model:page-size="state.pageInput.pageSize"
:total="state.total"
:page-sizes="[10, 20, 50, 100]"
background
@size-change="onSizeChange"
@current-change="onCurrentChange"
layout="total, sizes, prev, pager, next, jumper"
/>
</div>
</el-card>
</div>
</pane>
<pane>
<div class="my-flex-column w100 h100">
<el-card class="my-query-box mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
<el-form :inline="true" @submit.stop.prevent>
<el-form-item label="姓名">
<el-input v-model="state.filter.name" placeholder="姓名" @keyup.enter="onGetMsgUserList" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="ele-Search" @click="onGetMsgUserList"> 查询 </el-button>
<el-button v-auth="'api:admin:msg:add-msg-user'" type="primary" icon="ele-Plus" @click="onAddUser"> 添加 </el-button>
<el-button v-auth="'api:admin:msg:remove-msg-user'" type="danger" icon="ele-Delete" @click="onRemoveUser"> 移除 </el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="my-fill mt8" shadow="never">
<el-table
ref="userTableRef"
v-loading="state.userListLoading"
:data="state.userListData"
row-key="id"
style="width: 100%"
@row-click="onUserRowClick"
border
>
<el-table-column type="selection" width="55" />
<el-table-column prop="name" label="姓名" min-width="120" show-overflow-tooltip />
<el-table-column label="是否已读" width="100" align="center">
<template #default="{ row }">
<el-tag type="success" v-if="row.isRead">已读</el-tag>
<el-tag type="info" v-else>未读</el-tag>
</template>
</el-table-column>
<el-table-column prop="readTime" label="已读时间" :formatter="formatterTime" min-width="160" show-overflow-tooltip />
<!-- <el-table-column prop="mobile" label="手机号" min-width="120" show-overflow-tooltip />
<el-table-column prop="email" label="邮箱" min-width="180" show-overflow-tooltip /> -->
</el-table>
</el-card>
</div>
</pane>
<msg-form ref="msgFormRef" :title="state.msgFormTitle"></msg-form>
<user-select
ref="userSelectRef"
:title="`添加【${state.msgName}】员工`"
multiple
:sure-loading="state.sureLoading"
@sure="onSureUser"
></user-select>
</MySplitPanes>
</template>
<script lang="ts" setup name="admin/msg">
import { ref, reactive, onMounted, getCurrentInstance, onBeforeMount, defineAsyncComponent } from 'vue'
import {
PageInputMsgGetPageInput,
MsgGetMsgUserListOutput,
UserGetPageOutput,
MsgAddMsgUserListInput,
MsgGetPageOutput,
} from '/@/api/admin/data-contracts'
import { MsgApi } from '/@/api/admin/Msg'
import { ElTable } from 'element-plus'
import eventBus from '/@/utils/mitt'
import { auth } from '/@/utils/authFunction'
import { Pane } from 'splitpanes'
import dayjs from 'dayjs'
import { MsgStatusEnum } from '/@/api/admin/enum-contracts'
import { getDescByValue } from '/@/utils/enum'
//
const MsgForm = defineAsyncComponent(() => import('./components/msg-form.vue'))
const UserSelect = defineAsyncComponent(() => import('/@/views/admin/user/components/user-select.vue'))
const MySplitPanes = defineAsyncComponent(() => import('/@/components/my-layout/split-panes.vue'))
const MyDropdownMore = defineAsyncComponent(() => import('/@/components/my-dropdown-more/index.vue'))
const { proxy } = getCurrentInstance() as any
const msgTableRef = ref()
const msgFormRef = ref()
const userTableRef = ref<InstanceType<typeof ElTable>>()
const userSelectRef = ref()
const state = reactive({
loading: false,
userListLoading: false,
sureLoading: false,
msgFormTitle: '',
filter: {
name: '',
msgName: '',
},
total: 0,
pageInput: {
currentPage: 1,
pageSize: 20,
filter: {
title: '',
msgTypeValue: 690213836861509,
},
} as PageInputMsgGetPageInput,
msgData: [] as any,
userListData: [] as MsgGetMsgUserListOutput[],
msgId: undefined as number | undefined,
msgName: '' as string | null | undefined,
})
onMounted(async () => {
onQuery()
eventBus.off('refreshMsg')
eventBus.on('refreshMsg', () => {
onQuery()
})
})
onBeforeMount(() => {
eventBus.off('refreshMsg')
})
const formatterMsgStatusEnum = (row: any, column: any, cellValue: any) => {
return getDescByValue(MsgStatusEnum, cellValue)
}
const formatterTime = (row: any, column: any, cellValue: any) => {
return cellValue ? dayjs(cellValue).format('YYYY-MM-DD HH:mm:ss') : ''
}
const onQuery = async () => {
state.loading = true
if (state.pageInput.filter) state.pageInput.filter.title = state.filter.msgName
const res = await new MsgApi().getPage(state.pageInput).catch(() => {
state.loading = false
})
state.msgData = res?.data?.list ?? []
state.total = res?.data?.total ?? 0
if (state.msgData?.length > 0) {
window.setTimeout(() => {
msgTableRef.value?.setCurrentRow(state.msgData[0])
}, 100)
// nextTick(() => {
// msgTableRef.value?.setCurrentRow(state.msgData[0])
// })
}
state.loading = false
}
const onSizeChange = (val: number) => {
state.pageInput.currentPage = 1
state.pageInput.pageSize = val
onQuery()
}
const onCurrentChange = (val: number) => {
state.pageInput.currentPage = val
onQuery()
}
const onAdd = () => {
state.msgFormTitle = '新增设备消息'
msgFormRef.value.open({ enabled: true })
}
const onEdit = (row: MsgGetPageOutput) => {
state.msgFormTitle = '编辑设备消息'
msgFormRef.value.open(row)
}
const onDelete = (row: MsgGetPageOutput) => {
proxy.$modal
.confirmDelete(`确定要删除消息【${row.title}】?`)
.then(async () => {
await new MsgApi().delete({ id: row.id }, { loading: true, showSuccessMessage: true })
onQuery()
})
.catch(() => {})
}
const onGetMsgUserList = async () => {
state.userListLoading = true
const res = await new MsgApi().getMsgUserList({ MsgId: state.msgId, Name: state.filter.name }).catch(() => {
state.userListLoading = false
})
state.userListLoading = false
if (res?.success) {
if (res.data && res.data.length > 0) {
state.userListData = res.data
} else {
state.userListData = []
}
}
}
const onTableCurrentChange = (currentRow: MsgGetPageOutput) => {
if (!currentRow) {
return
}
state.msgId = currentRow.id
state.msgName = currentRow.title
onGetMsgUserList()
}
const onUserRowClick = (row: MsgGetMsgUserListOutput) => {
userTableRef.value!.toggleRowSelection(row, undefined)
}
const onAddUser = () => {
if (!((state.msgId as number) > 0)) {
proxy.$modal.msgWarning('请选择消息')
return
}
userSelectRef.value.open({ msgId: state.msgId })
}
const onRemoveUser = () => {
if (!((state.msgId as number) > 0)) {
proxy.$modal.msgWarning('请选择消息')
return
}
const selectionRows = userTableRef.value!.getSelectionRows() as UserGetPageOutput[]
if (!((selectionRows.length as number) > 0)) {
proxy.$modal.msgWarning('请选择员工')
return
}
proxy.$modal
.confirm(`确定要移除吗?`)
.then(async () => {
const userIds = selectionRows?.map((a) => a.id)
const input = { msgId: state.msgId, userIds } as MsgAddMsgUserListInput
await new MsgApi().removeMsgUser(input, { loading: true })
onGetMsgUserList()
})
.catch(() => {})
}
const onSureUser = async (users: UserGetPageOutput[]) => {
if (!(users?.length > 0)) {
userSelectRef.value.close()
return
}
state.sureLoading = true
const userIds = users?.map((a) => a.id)
const input = { msgId: state.msgId, userIds } as MsgAddMsgUserListInput
await new MsgApi().addMsgUser(input, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
state.sureLoading = false
userSelectRef.value.close()
onGetMsgUserList()
}
</script>
<style scoped lang="scss"></style>

View File

@ -1,253 +0,0 @@
<template>
<div>
<el-dialog
v-model="state.showDialog"
destroy-on-close
:title="title"
draggable
:close-on-click-modal="false"
:close-on-press-escape="false"
width="830px"
>
<el-form :model="form" ref="formRef" label-width="auto">
<el-row :gutter="35">
<!-- <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="分类" prop="typeId" :rules="[{ required: true, message: '请选择分类', trigger: ['change'] }]" hidden>
<el-tree-select
v-model="form.typeId"
placeholder="请选择分类"
:data="state.msgTypeTreeData"
node-key="id"
:props="{ label: 'name' }"
default-expand-all
fit-input-width
clearable
filterable
class="w100"
/>
</el-form-item>
</el-col> -->
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="标题" prop="title" :rules="[{ required: true, message: '请输入标题', trigger: ['blur', 'change'] }]">
<el-input v-model="form.title" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item
label="内容"
prop="content"
:rules="[
{ required: true, message: '请输入内容', trigger: ['blur', 'change'] },
{ validator: testEditorContent, trigger: ['blur', 'change'] },
]"
>
<MyEditor ref="editorRef" v-model:model-value="form.content" @onBlur="onValidateContent" @onChange="onValidateContent"></MyEditor>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
<el-form-item label="状态" prop="status" :rules="[{ required: true, message: '请选择状态', trigger: ['change'] }]">
<el-select v-model="form.status" placeholder="请选择状态" class="w100" @change="onStatusChange">
<el-option v-for="item in state.msgStatusList" :key="item.label" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" v-if="isScheduledStatus">
<el-form-item
label="发布时间"
prop="publishTime"
:rules="[{
required: isScheduledStatus,
message: '请选择发布时间',
trigger: ['change', 'blur']
}]"
>
<el-date-picker
v-model="form.publishTime"
type="datetime"
placeholder="请选择发布时间"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
:disabled-date="disabledDate"
class="w100"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel"> </el-button>
<el-button type="primary" @click="onSure" :loading="state.sureLoading"> </el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup name="admin/msg/form">
import { reactive, toRefs, ref, defineAsyncComponent, getCurrentInstance, computed } from 'vue'
import { MsgUpdateInput, MsgTypeGetListOutput } from '/@/api/admin/data-contracts'
// MsgUpdateInput
interface ExtendedMsgUpdateInput extends MsgUpdateInput {
publishTime?: string
}
import { MsgApi } from '/@/api/admin/Msg'
import { cloneDeep } from 'lodash-es'
import eventBus from '/@/utils/mitt'
import { MsgTypeApi } from '/@/api/admin/MsgType'
import { listToTree } from '/@/utils/tree'
import { MsgStatusEnum } from '/@/api/admin/enum-contracts'
import { toOptionsByValue } from '/@/utils/enum'
const MyEditor = defineAsyncComponent(() => import('/@/components/my-editor/index.vue'))
defineProps({
title: {
type: String,
default: '',
},
})
const formRef = ref()
const editorRef = ref()
const state = reactive({
showDialog: false,
sureLoading: false,
form: { content: '' } as ExtendedMsgUpdateInput,
msgTypeTreeData: [] as MsgTypeGetListOutput[],
v: null,
msgStatusList: toOptionsByValue(MsgStatusEnum),
})
const { proxy } = getCurrentInstance() as any
const { form } = toRefs(state)
//
const isScheduledStatus = computed(() => {
return state.form.status === 3 // 3 Scheduled
})
const testEditorContent = (rule: any, value: any, callback: any) => {
if (!value) {
callback()
}
if (editorRef.value.isEmpty()) {
callback(new Error('请输入内容'))
} else {
callback()
}
}
const onValidateContent = () => {
formRef.value.validateField('content')
}
//
const onStatusChange = (value: number) => {
if (value !== 3) { // 3
state.form.publishTime = undefined
//
formRef.value?.clearValidate('publishTime')
}
}
//
const disabledDate = (time: Date) => {
return time.getTime() < Date.now() - 86400000 //
}
const getMsgTypes = async () => {
const res = await new MsgTypeApi().getList().catch(() => {
state.msgTypeTreeData = []
})
if (res?.success && res.data && res.data.length > 0) {
state.msgTypeTreeData = listToTree(res.data)
} else {
state.msgTypeTreeData = []
}
}
//
const open = async (row: ExtendedMsgUpdateInput = { id: 0 }) => {
proxy.$modal.loading()
await getMsgTypes()
let formData = cloneDeep(row) as ExtendedMsgUpdateInput
if (row.id > 0) {
const res = await new MsgApi().get({ id: row.id }).catch(() => {
proxy.$modal.closeLoading()
})
if (res?.success) {
formData = res.data as ExtendedMsgUpdateInput
formData.typeId = formData.typeId && formData.typeId > 0 ? formData.typeId : undefined
}
}
state.form = formData
state.form.typeId = 690213625348165
proxy.$modal.closeLoading()
state.showDialog = true
}
//
const onCancel = () => {
state.showDialog = false
}
//
const onSure = () => {
formRef.value.validate(async (valid: boolean) => {
if (!valid) return
//
if (isScheduledStatus.value) {
if (!state.form.publishTime) {
proxy.$message.error('选择定时发布时必须选择发布时间')
return
}
const publishTime = new Date(state.form.publishTime)
const now = new Date()
if (publishTime <= now) {
proxy.$message.error('发布时间必须晚于当前时间')
return
}
} else {
//
state.form.publishTime = undefined
}
state.sureLoading = true
let res = {} as any
if (state.form.id != undefined && state.form.id > 0) {
res = await new MsgApi().update(state.form, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
} else {
res = await new MsgApi().add(state.form, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
}
state.sureLoading = false
if (res?.success) {
eventBus.emit('refreshMsg')
state.showDialog = false
}
})
}
defineExpose({
open,
})
</script>
<style lang="scss" scoped></style>

View File

@ -1,319 +0,0 @@
<template>
<MySplitPanes>
<pane size="55" min-size="35" max-size="65">
<div class="my-flex-column w100 h100">
<el-card class="my-query-box mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
<el-form :inline="true" @submit.stop.prevent>
<el-form-item label="标题">
<el-input v-model="state.filter.msgName" placeholder="标题" @keyup.enter="onQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="ele-Search" @click="onQuery"> 查询 </el-button>
<el-button v-auth="'api:admin:msg:add'" type="primary" icon="ele-Plus" @click="onAdd"> 新增 </el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="my-fill mt8" shadow="never">
<el-table
ref="msgTableRef"
v-loading="state.loading"
:data="state.msgData"
default-expand-all
highlight-current-row
style="width: 100%"
@current-change="onTableCurrentChange"
border
>
<el-table-column prop="title" label="标题" min-width="120" show-overflow-tooltip />
<el-table-column prop="typeName" label="消息分类" min-width="120" show-overflow-tooltip />
<el-table-column prop="status" label="状态" min-width="90" show-overflow-tooltip :formatter="formatterMsgStatusEnum" />
<el-table-column prop="createdTime" label="创建时间" :formatter="formatterTime" min-width="160" show-overflow-tooltip />
<el-table-column label="操作" width="100" fixed="right" header-align="center" align="center">
<template #default="{ row }">
<my-dropdown-more v-auths="['api:admin:msg:update', 'api:admin:msg:delete']" style="margin-left: 0px">
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item v-if="auth('api:admin:msg:update')" @click="onEdit(row)">编辑</el-dropdown-item>
<el-dropdown-item v-if="auth('api:admin:msg:delete')" @click="onDelete(row)">删除</el-dropdown-item>
</el-dropdown-menu>
</template>
</my-dropdown-more>
</template>
</el-table-column>
</el-table>
<div class="my-flex my-flex-end" style="margin-top: 10px">
<el-pagination
v-model:currentPage="state.pageInput.currentPage"
v-model:page-size="state.pageInput.pageSize"
:total="state.total"
:page-sizes="[10, 20, 50, 100]"
background
@size-change="onSizeChange"
@current-change="onCurrentChange"
layout="total, sizes, prev, pager, next, jumper"
/>
</div>
</el-card>
</div>
</pane>
<pane>
<div class="my-flex-column w100 h100">
<el-card class="my-query-box mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
<el-form :inline="true" @submit.stop.prevent>
<el-form-item label="姓名">
<el-input v-model="state.filter.name" placeholder="姓名" @keyup.enter="onGetMsgUserList" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="ele-Search" @click="onGetMsgUserList"> 查询 </el-button>
<el-button v-auth="'api:admin:msg:add-msg-user'" type="primary" icon="ele-Plus" @click="onAddUser"> 添加 </el-button>
<el-button v-auth="'api:admin:msg:remove-msg-user'" type="danger" icon="ele-Delete" @click="onRemoveUser"> 移除 </el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="my-fill mt8" shadow="never">
<el-table
ref="userTableRef"
v-loading="state.userListLoading"
:data="state.userListData"
row-key="id"
style="width: 100%"
@row-click="onUserRowClick"
border
>
<el-table-column type="selection" width="55" />
<el-table-column prop="name" label="姓名" min-width="120" show-overflow-tooltip />
<el-table-column label="是否已读" width="100" align="center">
<template #default="{ row }">
<el-tag type="success" v-if="row.isRead">已读</el-tag>
<el-tag type="info" v-else>未读</el-tag>
</template>
</el-table-column>
<el-table-column prop="readTime" label="已读时间" :formatter="formatterTime" min-width="160" show-overflow-tooltip />
<!-- <el-table-column prop="mobile" label="手机号" min-width="120" show-overflow-tooltip />
<el-table-column prop="email" label="邮箱" min-width="180" show-overflow-tooltip /> -->
</el-table>
</el-card>
</div>
</pane>
<msg-form ref="msgFormRef" :title="state.msgFormTitle"></msg-form>
<user-select
ref="userSelectRef"
:title="`添加【${state.msgName}】员工`"
multiple
:sure-loading="state.sureLoading"
@sure="onSureUser"
></user-select>
</MySplitPanes>
</template>
<script lang="ts" setup name="admin/msg">
import { ref, reactive, onMounted, getCurrentInstance, onBeforeMount, defineAsyncComponent } from 'vue'
import {
PageInputMsgGetPageInput,
MsgGetMsgUserListOutput,
UserGetPageOutput,
MsgAddMsgUserListInput,
MsgGetPageOutput,
} from '/@/api/admin/data-contracts'
import { MsgApi } from '/@/api/admin/Msg'
import { ElTable } from 'element-plus'
import eventBus from '/@/utils/mitt'
import { auth } from '/@/utils/authFunction'
import { Pane } from 'splitpanes'
import dayjs from 'dayjs'
import { MsgStatusEnum } from '/@/api/admin/enum-contracts'
import { getDescByValue } from '/@/utils/enum'
//
const MsgForm = defineAsyncComponent(() => import('./components/msg-form.vue'))
const UserSelect = defineAsyncComponent(() => import('/@/views/admin/user/components/user-select.vue'))
const MySplitPanes = defineAsyncComponent(() => import('/@/components/my-layout/split-panes.vue'))
const MyDropdownMore = defineAsyncComponent(() => import('/@/components/my-dropdown-more/index.vue'))
const { proxy } = getCurrentInstance() as any
const msgTableRef = ref()
const msgFormRef = ref()
const userTableRef = ref<InstanceType<typeof ElTable>>()
const userSelectRef = ref()
const state = reactive({
loading: false,
userListLoading: false,
sureLoading: false,
msgFormTitle: '',
filter: {
name: '',
msgName: '',
},
total: 0,
pageInput: {
currentPage: 1,
pageSize: 20,
filter: {
title: '',
msgTypeValue: 690213625348165,
},
} as PageInputMsgGetPageInput,
msgData: [] as any,
userListData: [] as MsgGetMsgUserListOutput[],
msgId: undefined as number | undefined,
msgName: '' as string | null | undefined,
})
onMounted(async () => {
onQuery()
eventBus.off('refreshMsg')
eventBus.on('refreshMsg', () => {
onQuery()
})
})
onBeforeMount(() => {
eventBus.off('refreshMsg')
})
const formatterMsgStatusEnum = (row: any, column: any, cellValue: any) => {
return getDescByValue(MsgStatusEnum, cellValue)
}
const formatterTime = (row: any, column: any, cellValue: any) => {
return cellValue ? dayjs(cellValue).format('YYYY-MM-DD HH:mm:ss') : ''
}
const onQuery = async () => {
state.loading = true
if (state.pageInput.filter) state.pageInput.filter.title = state.filter.msgName
const res = await new MsgApi().getPage(state.pageInput).catch(() => {
state.loading = false
})
state.msgData = res?.data?.list ?? []
state.total = res?.data?.total ?? 0
if (state.msgData?.length > 0) {
window.setTimeout(() => {
msgTableRef.value?.setCurrentRow(state.msgData[0])
}, 100)
// nextTick(() => {
// msgTableRef.value?.setCurrentRow(state.msgData[0])
// })
}
state.loading = false
}
const onSizeChange = (val: number) => {
state.pageInput.currentPage = 1
state.pageInput.pageSize = val
onQuery()
}
const onCurrentChange = (val: number) => {
state.pageInput.currentPage = val
onQuery()
}
const onAdd = () => {
state.msgFormTitle = '新增配液消息'
msgFormRef.value.open({ enabled: true })
}
const onEdit = (row: MsgGetPageOutput) => {
state.msgFormTitle = '编辑配液消息'
msgFormRef.value.open(row)
}
const onDelete = (row: MsgGetPageOutput) => {
proxy.$modal
.confirmDelete(`确定要删除消息【${row.title}】?`)
.then(async () => {
await new MsgApi().delete({ id: row.id }, { loading: true, showSuccessMessage: true })
onQuery()
})
.catch(() => {})
}
const onGetMsgUserList = async () => {
state.userListLoading = true
const res = await new MsgApi().getMsgUserList({ MsgId: state.msgId, Name: state.filter.name }).catch(() => {
state.userListLoading = false
})
state.userListLoading = false
if (res?.success) {
if (res.data && res.data.length > 0) {
state.userListData = res.data
} else {
state.userListData = []
}
}
}
const onTableCurrentChange = (currentRow: MsgGetPageOutput) => {
if (!currentRow) {
return
}
state.msgId = currentRow.id
state.msgName = currentRow.title
onGetMsgUserList()
}
const onUserRowClick = (row: MsgGetMsgUserListOutput) => {
userTableRef.value!.toggleRowSelection(row, undefined)
}
const onAddUser = () => {
if (!((state.msgId as number) > 0)) {
proxy.$modal.msgWarning('请选择消息')
return
}
userSelectRef.value.open({ msgId: state.msgId })
}
const onRemoveUser = () => {
if (!((state.msgId as number) > 0)) {
proxy.$modal.msgWarning('请选择消息')
return
}
const selectionRows = userTableRef.value!.getSelectionRows() as UserGetPageOutput[]
if (!((selectionRows.length as number) > 0)) {
proxy.$modal.msgWarning('请选择员工')
return
}
proxy.$modal
.confirm(`确定要移除吗?`)
.then(async () => {
const userIds = selectionRows?.map((a) => a.id)
const input = { msgId: state.msgId, userIds } as MsgAddMsgUserListInput
await new MsgApi().removeMsgUser(input, { loading: true })
onGetMsgUserList()
})
.catch(() => {})
}
const onSureUser = async (users: UserGetPageOutput[]) => {
if (!(users?.length > 0)) {
userSelectRef.value.close()
return
}
state.sureLoading = true
const userIds = users?.map((a) => a.id)
const input = { msgId: state.msgId, userIds } as MsgAddMsgUserListInput
await new MsgApi().addMsgUser(input, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
state.sureLoading = false
userSelectRef.value.close()
onGetMsgUserList()
}
</script>
<style scoped lang="scss"></style>

View File

@ -1,253 +0,0 @@
<template>
<div>
<el-dialog
v-model="state.showDialog"
destroy-on-close
:title="title"
draggable
:close-on-click-modal="false"
:close-on-press-escape="false"
width="830px"
>
<el-form :model="form" ref="formRef" label-width="auto">
<el-row :gutter="35">
<!-- <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="分类" prop="typeId" :rules="[{ required: true, message: '请选择分类', trigger: ['change'] }]" hidden>
<el-tree-select
v-model="form.typeId"
placeholder="请选择分类"
:data="state.msgTypeTreeData"
node-key="id"
:props="{ label: 'name' }"
default-expand-all
fit-input-width
clearable
filterable
class="w100"
/>
</el-form-item>
</el-col> -->
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="标题" prop="title" :rules="[{ required: true, message: '请输入标题', trigger: ['blur', 'change'] }]">
<el-input v-model="form.title" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item
label="内容"
prop="content"
:rules="[
{ required: true, message: '请输入内容', trigger: ['blur', 'change'] },
{ validator: testEditorContent, trigger: ['blur', 'change'] },
]"
>
<MyEditor ref="editorRef" v-model:model-value="form.content" @onBlur="onValidateContent" @onChange="onValidateContent"></MyEditor>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
<el-form-item label="状态" prop="status" :rules="[{ required: true, message: '请选择状态', trigger: ['change'] }]">
<el-select v-model="form.status" placeholder="请选择状态" class="w100" @change="onStatusChange">
<el-option v-for="item in state.msgStatusList" :key="item.label" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" v-if="isScheduledStatus">
<el-form-item
label="发布时间"
prop="publishTime"
:rules="[{
required: isScheduledStatus,
message: '请选择发布时间',
trigger: ['change', 'blur']
}]"
>
<el-date-picker
v-model="form.publishTime"
type="datetime"
placeholder="请选择发布时间"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
:disabled-date="disabledDate"
class="w100"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel"> </el-button>
<el-button type="primary" @click="onSure" :loading="state.sureLoading"> </el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup name="admin/msg/form">
import { reactive, toRefs, ref, defineAsyncComponent, getCurrentInstance, computed } from 'vue'
import { MsgUpdateInput, MsgTypeGetListOutput } from '/@/api/admin/data-contracts'
import { MsgApi } from '/@/api/admin/Msg'
import { cloneDeep } from 'lodash-es'
import eventBus from '/@/utils/mitt'
import { MsgTypeApi } from '/@/api/admin/MsgType'
import { listToTree } from '/@/utils/tree'
import { MsgStatusEnum } from '/@/api/admin/enum-contracts'
import { toOptionsByValue } from '/@/utils/enum'
const MyEditor = defineAsyncComponent(() => import('/@/components/my-editor/index.vue'))
// MsgUpdateInput
interface ExtendedMsgUpdateInput extends MsgUpdateInput {
publishTime?: string
}
defineProps({
title: {
type: String,
default: '',
},
})
const formRef = ref()
const editorRef = ref()
const state = reactive({
showDialog: false,
sureLoading: false,
form: { content: '' } as ExtendedMsgUpdateInput,
msgTypeTreeData: [] as MsgTypeGetListOutput[],
v: null,
msgStatusList: toOptionsByValue(MsgStatusEnum),
})
const { proxy } = getCurrentInstance() as any
const { form } = toRefs(state)
//
const isScheduledStatus = computed(() => {
return state.form.status === 3 // 3 Scheduled
})
const testEditorContent = (rule: any, value: any, callback: any) => {
if (!value) {
callback()
}
if (editorRef.value.isEmpty()) {
callback(new Error('请输入内容'))
} else {
callback()
}
}
const onValidateContent = () => {
formRef.value.validateField('content')
}
const getMsgTypes = async () => {
const res = await new MsgTypeApi().getList().catch(() => {
state.msgTypeTreeData = []
})
if (res?.success && res.data && res.data.length > 0) {
state.msgTypeTreeData = listToTree(res.data)
} else {
state.msgTypeTreeData = []
}
}
//
const onStatusChange = (value: number) => {
if (value !== 3) { // 3
state.form.publishTime = undefined
//
formRef.value?.clearValidate('publishTime')
}
}
//
const disabledDate = (time: Date) => {
return time.getTime() < Date.now() - 86400000 //
}
//
const open = async (row: ExtendedMsgUpdateInput = { id: 0 }) => {
proxy.$modal.loading()
await getMsgTypes()
let formData = cloneDeep(row) as ExtendedMsgUpdateInput
if (row.id > 0) {
const res = await new MsgApi().get({ id: row.id }).catch(() => {
proxy.$modal.closeLoading()
})
if (res?.success) {
formData = res.data as ExtendedMsgUpdateInput
formData.typeId = formData.typeId && formData.typeId > 0 ? formData.typeId : undefined
}
}
state.form = formData
state.form.typeId = 690213714493509
proxy.$modal.closeLoading()
state.showDialog = true
}
//
const onCancel = () => {
state.showDialog = false
}
//
const onSure = () => {
formRef.value.validate(async (valid: boolean) => {
if (!valid) return
//
if (isScheduledStatus.value) {
if (!state.form.publishTime) {
proxy.$message.error('选择定时发布时必须选择发布时间')
return
}
const publishTime = new Date(state.form.publishTime)
const now = new Date()
if (publishTime <= now) {
proxy.$message.error('发布时间必须晚于当前时间')
return
}
} else {
//
state.form.publishTime = undefined
}
state.sureLoading = true
let res = {} as any
if (state.form.id != undefined && state.form.id > 0) {
res = await new MsgApi().update(state.form, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
} else {
res = await new MsgApi().add(state.form, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
}
state.sureLoading = false
if (res?.success) {
eventBus.emit('refreshMsg')
state.showDialog = false
}
})
}
defineExpose({
open,
})
</script>
<style lang="scss" scoped></style>

View File

@ -1,319 +0,0 @@
<template>
<MySplitPanes>
<pane size="55" min-size="35" max-size="65">
<div class="my-flex-column w100 h100">
<el-card class="my-query-box mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
<el-form :inline="true" @submit.stop.prevent>
<el-form-item label="标题">
<el-input v-model="state.filter.msgName" placeholder="标题" @keyup.enter="onQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="ele-Search" @click="onQuery"> 查询 </el-button>
<el-button v-auth="'api:admin:msg:add'" type="primary" icon="ele-Plus" @click="onAdd"> 新增 </el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="my-fill mt8" shadow="never">
<el-table
ref="msgTableRef"
v-loading="state.loading"
:data="state.msgData"
default-expand-all
highlight-current-row
style="width: 100%"
@current-change="onTableCurrentChange"
border
>
<el-table-column prop="title" label="标题" min-width="120" show-overflow-tooltip />
<el-table-column prop="typeName" label="消息分类" min-width="120" show-overflow-tooltip />
<el-table-column prop="status" label="状态" min-width="90" show-overflow-tooltip :formatter="formatterMsgStatusEnum" />
<el-table-column prop="createdTime" label="创建时间" :formatter="formatterTime" min-width="160" show-overflow-tooltip />
<el-table-column label="操作" width="100" fixed="right" header-align="center" align="center">
<template #default="{ row }">
<my-dropdown-more v-auths="['api:admin:msg:update', 'api:admin:msg:delete']" style="margin-left: 0px">
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item v-if="auth('api:admin:msg:update')" @click="onEdit(row)">编辑</el-dropdown-item>
<el-dropdown-item v-if="auth('api:admin:msg:delete')" @click="onDelete(row)">删除</el-dropdown-item>
</el-dropdown-menu>
</template>
</my-dropdown-more>
</template>
</el-table-column>
</el-table>
<div class="my-flex my-flex-end" style="margin-top: 10px">
<el-pagination
v-model:currentPage="state.pageInput.currentPage"
v-model:page-size="state.pageInput.pageSize"
:total="state.total"
:page-sizes="[10, 20, 50, 100]"
background
@size-change="onSizeChange"
@current-change="onCurrentChange"
layout="total, sizes, prev, pager, next, jumper"
/>
</div>
</el-card>
</div>
</pane>
<pane>
<div class="my-flex-column w100 h100">
<el-card class="my-query-box mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
<el-form :inline="true" @submit.stop.prevent>
<el-form-item label="姓名">
<el-input v-model="state.filter.name" placeholder="姓名" @keyup.enter="onGetMsgUserList" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="ele-Search" @click="onGetMsgUserList"> 查询 </el-button>
<el-button v-auth="'api:admin:msg:add-msg-user'" type="primary" icon="ele-Plus" @click="onAddUser"> 添加 </el-button>
<el-button v-auth="'api:admin:msg:remove-msg-user'" type="danger" icon="ele-Delete" @click="onRemoveUser"> 移除 </el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="my-fill mt8" shadow="never">
<el-table
ref="userTableRef"
v-loading="state.userListLoading"
:data="state.userListData"
row-key="id"
style="width: 100%"
@row-click="onUserRowClick"
border
>
<el-table-column type="selection" width="55" />
<el-table-column prop="name" label="姓名" min-width="120" show-overflow-tooltip />
<el-table-column label="是否已读" width="100" align="center">
<template #default="{ row }">
<el-tag type="success" v-if="row.isRead">已读</el-tag>
<el-tag type="info" v-else>未读</el-tag>
</template>
</el-table-column>
<el-table-column prop="readTime" label="已读时间" :formatter="formatterTime" min-width="160" show-overflow-tooltip />
<!-- <el-table-column prop="mobile" label="手机号" min-width="120" show-overflow-tooltip />
<el-table-column prop="email" label="邮箱" min-width="180" show-overflow-tooltip /> -->
</el-table>
</el-card>
</div>
</pane>
<msg-form ref="msgFormRef" :title="state.msgFormTitle"></msg-form>
<user-select
ref="userSelectRef"
:title="`添加【${state.msgName}】员工`"
multiple
:sure-loading="state.sureLoading"
@sure="onSureUser"
></user-select>
</MySplitPanes>
</template>
<script lang="ts" setup name="admin/msg">
import { ref, reactive, onMounted, getCurrentInstance, onBeforeMount, defineAsyncComponent } from 'vue'
import {
PageInputMsgGetPageInput,
MsgGetMsgUserListOutput,
UserGetPageOutput,
MsgAddMsgUserListInput,
MsgGetPageOutput,
} from '/@/api/admin/data-contracts'
import { MsgApi } from '/@/api/admin/Msg'
import { ElTable } from 'element-plus'
import eventBus from '/@/utils/mitt'
import { auth } from '/@/utils/authFunction'
import { Pane } from 'splitpanes'
import dayjs from 'dayjs'
import { MsgStatusEnum } from '/@/api/admin/enum-contracts'
import { getDescByValue } from '/@/utils/enum'
//
const MsgForm = defineAsyncComponent(() => import('./components/msg-form.vue'))
const UserSelect = defineAsyncComponent(() => import('/@/views/admin/user/components/user-select.vue'))
const MySplitPanes = defineAsyncComponent(() => import('/@/components/my-layout/split-panes.vue'))
const MyDropdownMore = defineAsyncComponent(() => import('/@/components/my-dropdown-more/index.vue'))
const { proxy } = getCurrentInstance() as any
const msgTableRef = ref()
const msgFormRef = ref()
const userTableRef = ref<InstanceType<typeof ElTable>>()
const userSelectRef = ref()
const state = reactive({
loading: false,
userListLoading: false,
sureLoading: false,
msgFormTitle: '',
filter: {
name: '',
msgName: '',
},
total: 0,
pageInput: {
currentPage: 1,
pageSize: 20,
filter: {
title: '',
msgTypeValue: 690213714493509,
},
} as PageInputMsgGetPageInput,
msgData: [] as any,
userListData: [] as MsgGetMsgUserListOutput[],
msgId: undefined as number | undefined,
msgName: '' as string | null | undefined,
})
onMounted(async () => {
onQuery()
eventBus.off('refreshMsg')
eventBus.on('refreshMsg', () => {
onQuery()
})
})
onBeforeMount(() => {
eventBus.off('refreshMsg')
})
const formatterMsgStatusEnum = (row: any, column: any, cellValue: any) => {
return getDescByValue(MsgStatusEnum, cellValue)
}
const formatterTime = (row: any, column: any, cellValue: any) => {
return cellValue ? dayjs(cellValue).format('YYYY-MM-DD HH:mm:ss') : ''
}
const onQuery = async () => {
state.loading = true
if (state.pageInput.filter) state.pageInput.filter.title = state.filter.msgName
const res = await new MsgApi().getPage(state.pageInput).catch(() => {
state.loading = false
})
state.msgData = res?.data?.list ?? []
state.total = res?.data?.total ?? 0
if (state.msgData?.length > 0) {
window.setTimeout(() => {
msgTableRef.value?.setCurrentRow(state.msgData[0])
}, 100)
// nextTick(() => {
// msgTableRef.value?.setCurrentRow(state.msgData[0])
// })
}
state.loading = false
}
const onSizeChange = (val: number) => {
state.pageInput.currentPage = 1
state.pageInput.pageSize = val
onQuery()
}
const onCurrentChange = (val: number) => {
state.pageInput.currentPage = val
onQuery()
}
const onAdd = () => {
state.msgFormTitle = '新增物料消息'
msgFormRef.value.open({ enabled: true })
}
const onEdit = (row: MsgGetPageOutput) => {
state.msgFormTitle = '编辑物料消息'
msgFormRef.value.open(row)
}
const onDelete = (row: MsgGetPageOutput) => {
proxy.$modal
.confirmDelete(`确定要删除消息【${row.title}】?`)
.then(async () => {
await new MsgApi().delete({ id: row.id }, { loading: true, showSuccessMessage: true })
onQuery()
})
.catch(() => {})
}
const onGetMsgUserList = async () => {
state.userListLoading = true
const res = await new MsgApi().getMsgUserList({ MsgId: state.msgId, Name: state.filter.name }).catch(() => {
state.userListLoading = false
})
state.userListLoading = false
if (res?.success) {
if (res.data && res.data.length > 0) {
state.userListData = res.data
} else {
state.userListData = []
}
}
}
const onTableCurrentChange = (currentRow: MsgGetPageOutput) => {
if (!currentRow) {
return
}
state.msgId = currentRow.id
state.msgName = currentRow.title
onGetMsgUserList()
}
const onUserRowClick = (row: MsgGetMsgUserListOutput) => {
userTableRef.value!.toggleRowSelection(row, undefined)
}
const onAddUser = () => {
if (!((state.msgId as number) > 0)) {
proxy.$modal.msgWarning('请选择消息')
return
}
userSelectRef.value.open({ msgId: state.msgId })
}
const onRemoveUser = () => {
if (!((state.msgId as number) > 0)) {
proxy.$modal.msgWarning('请选择消息')
return
}
const selectionRows = userTableRef.value!.getSelectionRows() as UserGetPageOutput[]
if (!((selectionRows.length as number) > 0)) {
proxy.$modal.msgWarning('请选择员工')
return
}
proxy.$modal
.confirm(`确定要移除吗?`)
.then(async () => {
const userIds = selectionRows?.map((a) => a.id)
const input = { msgId: state.msgId, userIds } as MsgAddMsgUserListInput
await new MsgApi().removeMsgUser(input, { loading: true })
onGetMsgUserList()
})
.catch(() => {})
}
const onSureUser = async (users: UserGetPageOutput[]) => {
if (!(users?.length > 0)) {
userSelectRef.value.close()
return
}
state.sureLoading = true
const userIds = users?.map((a) => a.id)
const input = { msgId: state.msgId, userIds } as MsgAddMsgUserListInput
await new MsgApi().addMsgUser(input, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
state.sureLoading = false
userSelectRef.value.close()
onGetMsgUserList()
}
</script>
<style scoped lang="scss"></style>

View File

@ -1,253 +0,0 @@
<template>
<div>
<el-dialog
v-model="state.showDialog"
destroy-on-close
:title="title"
draggable
:close-on-click-modal="false"
:close-on-press-escape="false"
width="830px"
>
<el-form :model="form" ref="formRef" label-width="auto">
<el-row :gutter="35">
<!-- <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="分类" prop="typeId" :rules="[{ required: true, message: '请选择分类', trigger: ['change'] }]" hidden>
<el-tree-select
v-model="form.typeId"
placeholder="请选择分类"
:data="state.msgTypeTreeData"
node-key="id"
:props="{ label: 'name' }"
default-expand-all
fit-input-width
clearable
filterable
class="w100"
/>
</el-form-item>
</el-col> -->
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="标题" prop="title" :rules="[{ required: true, message: '请输入标题', trigger: ['blur', 'change'] }]">
<el-input v-model="form.title" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item
label="内容"
prop="content"
:rules="[
{ required: true, message: '请输入内容', trigger: ['blur', 'change'] },
{ validator: testEditorContent, trigger: ['blur', 'change'] },
]"
>
<MyEditor ref="editorRef" v-model:model-value="form.content" @onBlur="onValidateContent" @onChange="onValidateContent"></MyEditor>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
<el-form-item label="状态" prop="status" :rules="[{ required: true, message: '请选择状态', trigger: ['change'] }]">
<el-select v-model="form.status" placeholder="请选择状态" class="w100" @change="onStatusChange">
<el-option v-for="item in state.msgStatusList" :key="item.label" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" v-if="isScheduledStatus">
<el-form-item
label="发布时间"
prop="publishTime"
:rules="[{
required: isScheduledStatus,
message: '请选择发布时间',
trigger: ['change', 'blur']
}]"
>
<el-date-picker
v-model="form.publishTime"
type="datetime"
placeholder="请选择发布时间"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
:disabled-date="disabledDate"
class="w100"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel"> </el-button>
<el-button type="primary" @click="onSure" :loading="state.sureLoading"> </el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup name="admin/msg/form">
import { reactive, toRefs, ref, defineAsyncComponent, getCurrentInstance, computed } from 'vue'
import { MsgUpdateInput, MsgTypeGetListOutput } from '/@/api/admin/data-contracts'
import { MsgApi } from '/@/api/admin/Msg'
import { cloneDeep } from 'lodash-es'
import eventBus from '/@/utils/mitt'
import { MsgTypeApi } from '/@/api/admin/MsgType'
import { listToTree } from '/@/utils/tree'
import { MsgStatusEnum } from '/@/api/admin/enum-contracts'
import { toOptionsByValue } from '/@/utils/enum'
const MyEditor = defineAsyncComponent(() => import('/@/components/my-editor/index.vue'))
// MsgUpdateInput
interface ExtendedMsgUpdateInput extends MsgUpdateInput {
publishTime?: string
}
defineProps({
title: {
type: String,
default: '',
},
})
const formRef = ref()
const editorRef = ref()
const state = reactive({
showDialog: false,
sureLoading: false,
form: { content: '' } as ExtendedMsgUpdateInput,
msgTypeTreeData: [] as MsgTypeGetListOutput[],
v: null,
msgStatusList: toOptionsByValue(MsgStatusEnum),
})
const { proxy } = getCurrentInstance() as any
const { form } = toRefs(state)
//
const isScheduledStatus = computed(() => {
return state.form.status === 3 // 3 Scheduled
})
const testEditorContent = (rule: any, value: any, callback: any) => {
if (!value) {
callback()
}
if (editorRef.value.isEmpty()) {
callback(new Error('请输入内容'))
} else {
callback()
}
}
const onValidateContent = () => {
formRef.value.validateField('content')
}
const getMsgTypes = async () => {
const res = await new MsgTypeApi().getList().catch(() => {
state.msgTypeTreeData = []
})
if (res?.success && res.data && res.data.length > 0) {
state.msgTypeTreeData = listToTree(res.data)
} else {
state.msgTypeTreeData = []
}
}
//
const onStatusChange = (value: number) => {
if (value !== 3) { // 3
state.form.publishTime = undefined
//
formRef.value?.clearValidate('publishTime')
}
}
//
const disabledDate = (time: Date) => {
return time.getTime() < Date.now() - 86400000 //
}
//
const open = async (row: ExtendedMsgUpdateInput = { id: 0 }) => {
proxy.$modal.loading()
await getMsgTypes()
let formData = cloneDeep(row) as ExtendedMsgUpdateInput
if (row.id > 0) {
const res = await new MsgApi().get({ id: row.id }).catch(() => {
proxy.$modal.closeLoading()
})
if (res?.success) {
formData = res.data as ExtendedMsgUpdateInput
formData.typeId = formData.typeId && formData.typeId > 0 ? formData.typeId : undefined
}
}
state.form = formData
state.form.typeId = 690214203293765
proxy.$modal.closeLoading()
state.showDialog = true
}
//
const onCancel = () => {
state.showDialog = false
}
//
const onSure = () => {
formRef.value.validate(async (valid: boolean) => {
if (!valid) return
//
if (isScheduledStatus.value) {
if (!state.form.publishTime) {
proxy.$message.error('选择定时发布时必须选择发布时间')
return
}
const publishTime = new Date(state.form.publishTime)
const now = new Date()
if (publishTime <= now) {
proxy.$message.error('发布时间必须晚于当前时间')
return
}
} else {
//
state.form.publishTime = undefined
}
state.sureLoading = true
let res = {} as any
if (state.form.id != undefined && state.form.id > 0) {
res = await new MsgApi().update(state.form, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
} else {
res = await new MsgApi().add(state.form, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
}
state.sureLoading = false
if (res?.success) {
eventBus.emit('refreshMsg')
state.showDialog = false
}
})
}
defineExpose({
open,
})
</script>
<style lang="scss" scoped></style>

View File

@ -1,319 +0,0 @@
<template>
<MySplitPanes>
<pane size="55" min-size="35" max-size="65">
<div class="my-flex-column w100 h100">
<el-card class="my-query-box mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
<el-form :inline="true" @submit.stop.prevent>
<el-form-item label="标题">
<el-input v-model="state.filter.msgName" placeholder="标题" @keyup.enter="onQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="ele-Search" @click="onQuery"> 查询 </el-button>
<el-button v-auth="'api:admin:msg:add'" type="primary" icon="ele-Plus" @click="onAdd"> 新增 </el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="my-fill mt8" shadow="never">
<el-table
ref="msgTableRef"
v-loading="state.loading"
:data="state.msgData"
default-expand-all
highlight-current-row
style="width: 100%"
@current-change="onTableCurrentChange"
border
>
<el-table-column prop="title" label="标题" min-width="120" show-overflow-tooltip />
<el-table-column prop="typeName" label="消息分类" min-width="120" show-overflow-tooltip />
<el-table-column prop="status" label="状态" min-width="90" show-overflow-tooltip :formatter="formatterMsgStatusEnum" />
<el-table-column prop="createdTime" label="创建时间" :formatter="formatterTime" min-width="160" show-overflow-tooltip />
<el-table-column label="操作" width="100" fixed="right" header-align="center" align="center">
<template #default="{ row }">
<my-dropdown-more v-auths="['api:admin:msg:update', 'api:admin:msg:delete']" style="margin-left: 0px">
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item v-if="auth('api:admin:msg:update')" @click="onEdit(row)">编辑</el-dropdown-item>
<el-dropdown-item v-if="auth('api:admin:msg:delete')" @click="onDelete(row)">删除</el-dropdown-item>
</el-dropdown-menu>
</template>
</my-dropdown-more>
</template>
</el-table-column>
</el-table>
<div class="my-flex my-flex-end" style="margin-top: 10px">
<el-pagination
v-model:currentPage="state.pageInput.currentPage"
v-model:page-size="state.pageInput.pageSize"
:total="state.total"
:page-sizes="[10, 20, 50, 100]"
background
@size-change="onSizeChange"
@current-change="onCurrentChange"
layout="total, sizes, prev, pager, next, jumper"
/>
</div>
</el-card>
</div>
</pane>
<pane>
<div class="my-flex-column w100 h100">
<el-card class="my-query-box mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
<el-form :inline="true" @submit.stop.prevent>
<el-form-item label="姓名">
<el-input v-model="state.filter.name" placeholder="姓名" @keyup.enter="onGetMsgUserList" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="ele-Search" @click="onGetMsgUserList"> 查询 </el-button>
<el-button v-auth="'api:admin:msg:add-msg-user'" type="primary" icon="ele-Plus" @click="onAddUser"> 添加 </el-button>
<el-button v-auth="'api:admin:msg:remove-msg-user'" type="danger" icon="ele-Delete" @click="onRemoveUser"> 移除 </el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="my-fill mt8" shadow="never">
<el-table
ref="userTableRef"
v-loading="state.userListLoading"
:data="state.userListData"
row-key="id"
style="width: 100%"
@row-click="onUserRowClick"
border
>
<el-table-column type="selection" width="55" />
<el-table-column prop="name" label="姓名" min-width="120" show-overflow-tooltip />
<el-table-column label="是否已读" width="100" align="center">
<template #default="{ row }">
<el-tag type="success" v-if="row.isRead">已读</el-tag>
<el-tag type="info" v-else>未读</el-tag>
</template>
</el-table-column>
<el-table-column prop="readTime" label="已读时间" :formatter="formatterTime" min-width="160" show-overflow-tooltip />
<!-- <el-table-column prop="mobile" label="手机号" min-width="120" show-overflow-tooltip />
<el-table-column prop="email" label="邮箱" min-width="180" show-overflow-tooltip /> -->
</el-table>
</el-card>
</div>
</pane>
<msg-form ref="msgFormRef" :title="state.msgFormTitle"></msg-form>
<user-select
ref="userSelectRef"
:title="`添加【${state.msgName}】员工`"
multiple
:sure-loading="state.sureLoading"
@sure="onSureUser"
></user-select>
</MySplitPanes>
</template>
<script lang="ts" setup name="admin/msg">
import { ref, reactive, onMounted, getCurrentInstance, onBeforeMount, defineAsyncComponent } from 'vue'
import {
PageInputMsgGetPageInput,
MsgGetMsgUserListOutput,
UserGetPageOutput,
MsgAddMsgUserListInput,
MsgGetPageOutput,
} from '/@/api/admin/data-contracts'
import { MsgApi } from '/@/api/admin/Msg'
import { ElTable } from 'element-plus'
import eventBus from '/@/utils/mitt'
import { auth } from '/@/utils/authFunction'
import { Pane } from 'splitpanes'
import dayjs from 'dayjs'
import { MsgStatusEnum } from '/@/api/admin/enum-contracts'
import { getDescByValue } from '/@/utils/enum'
//
const MsgForm = defineAsyncComponent(() => import('./components/msg-form.vue'))
const UserSelect = defineAsyncComponent(() => import('/@/views/admin/user/components/user-select.vue'))
const MySplitPanes = defineAsyncComponent(() => import('/@/components/my-layout/split-panes.vue'))
const MyDropdownMore = defineAsyncComponent(() => import('/@/components/my-dropdown-more/index.vue'))
const { proxy } = getCurrentInstance() as any
const msgTableRef = ref()
const msgFormRef = ref()
const userTableRef = ref<InstanceType<typeof ElTable>>()
const userSelectRef = ref()
const state = reactive({
loading: false,
userListLoading: false,
sureLoading: false,
msgFormTitle: '',
filter: {
name: '',
msgName: '',
},
total: 0,
pageInput: {
currentPage: 1,
pageSize: 20,
filter: {
title: '',
msgTypeValue: 690214203293765,
},
} as PageInputMsgGetPageInput,
msgData: [] as any,
userListData: [] as MsgGetMsgUserListOutput[],
msgId: undefined as number | undefined,
msgName: '' as string | null | undefined,
})
onMounted(async () => {
onQuery()
eventBus.off('refreshMsg')
eventBus.on('refreshMsg', () => {
onQuery()
})
})
onBeforeMount(() => {
eventBus.off('refreshMsg')
})
const formatterMsgStatusEnum = (row: any, column: any, cellValue: any) => {
return getDescByValue(MsgStatusEnum, cellValue)
}
const formatterTime = (row: any, column: any, cellValue: any) => {
return cellValue ? dayjs(cellValue).format('YYYY-MM-DD HH:mm:ss') : ''
}
const onQuery = async () => {
state.loading = true
if (state.pageInput.filter) state.pageInput.filter.title = state.filter.msgName
const res = await new MsgApi().getPage(state.pageInput).catch(() => {
state.loading = false
})
state.msgData = res?.data?.list ?? []
state.total = res?.data?.total ?? 0
if (state.msgData?.length > 0) {
window.setTimeout(() => {
msgTableRef.value?.setCurrentRow(state.msgData[0])
}, 100)
// nextTick(() => {
// msgTableRef.value?.setCurrentRow(state.msgData[0])
// })
}
state.loading = false
}
const onSizeChange = (val: number) => {
state.pageInput.currentPage = 1
state.pageInput.pageSize = val
onQuery()
}
const onCurrentChange = (val: number) => {
state.pageInput.currentPage = val
onQuery()
}
const onAdd = () => {
state.msgFormTitle = '新增工艺消息'
msgFormRef.value.open({ enabled: true })
}
const onEdit = (row: MsgGetPageOutput) => {
state.msgFormTitle = '编辑工艺消息'
msgFormRef.value.open(row)
}
const onDelete = (row: MsgGetPageOutput) => {
proxy.$modal
.confirmDelete(`确定要删除消息【${row.title}】?`)
.then(async () => {
await new MsgApi().delete({ id: row.id }, { loading: true, showSuccessMessage: true })
onQuery()
})
.catch(() => {})
}
const onGetMsgUserList = async () => {
state.userListLoading = true
const res = await new MsgApi().getMsgUserList({ MsgId: state.msgId, Name: state.filter.name }).catch(() => {
state.userListLoading = false
})
state.userListLoading = false
if (res?.success) {
if (res.data && res.data.length > 0) {
state.userListData = res.data
} else {
state.userListData = []
}
}
}
const onTableCurrentChange = (currentRow: MsgGetPageOutput) => {
if (!currentRow) {
return
}
state.msgId = currentRow.id
state.msgName = currentRow.title
onGetMsgUserList()
}
const onUserRowClick = (row: MsgGetMsgUserListOutput) => {
userTableRef.value!.toggleRowSelection(row, undefined)
}
const onAddUser = () => {
if (!((state.msgId as number) > 0)) {
proxy.$modal.msgWarning('请选择消息')
return
}
userSelectRef.value.open({ msgId: state.msgId })
}
const onRemoveUser = () => {
if (!((state.msgId as number) > 0)) {
proxy.$modal.msgWarning('请选择消息')
return
}
const selectionRows = userTableRef.value!.getSelectionRows() as UserGetPageOutput[]
if (!((selectionRows.length as number) > 0)) {
proxy.$modal.msgWarning('请选择员工')
return
}
proxy.$modal
.confirm(`确定要移除吗?`)
.then(async () => {
const userIds = selectionRows?.map((a) => a.id)
const input = { msgId: state.msgId, userIds } as MsgAddMsgUserListInput
await new MsgApi().removeMsgUser(input, { loading: true })
onGetMsgUserList()
})
.catch(() => {})
}
const onSureUser = async (users: UserGetPageOutput[]) => {
if (!(users?.length > 0)) {
userSelectRef.value.close()
return
}
state.sureLoading = true
const userIds = users?.map((a) => a.id)
const input = { msgId: state.msgId, userIds } as MsgAddMsgUserListInput
await new MsgApi().addMsgUser(input, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
state.sureLoading = false
userSelectRef.value.close()
onGetMsgUserList()
}
</script>
<style scoped lang="scss"></style>

View File

@ -1,253 +0,0 @@
<template>
<div>
<el-dialog
v-model="state.showDialog"
destroy-on-close
:title="title"
draggable
:close-on-click-modal="false"
:close-on-press-escape="false"
width="830px"
>
<el-form :model="form" ref="formRef" label-width="auto">
<el-row :gutter="35">
<!-- <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="分类" prop="typeId" :rules="[{ required: true, message: '请选择分类', trigger: ['change'] }]" hidden>
<el-tree-select
v-model="form.typeId"
placeholder="请选择分类"
:data="state.msgTypeTreeData"
node-key="id"
:props="{ label: 'name' }"
default-expand-all
fit-input-width
clearable
filterable
class="w100"
/>
</el-form-item>
</el-col> -->
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="标题" prop="title" :rules="[{ required: true, message: '请输入标题', trigger: ['blur', 'change'] }]">
<el-input v-model="form.title" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item
label="内容"
prop="content"
:rules="[
{ required: true, message: '请输入内容', trigger: ['blur', 'change'] },
{ validator: testEditorContent, trigger: ['blur', 'change'] },
]"
>
<MyEditor ref="editorRef" v-model:model-value="form.content" @onBlur="onValidateContent" @onChange="onValidateContent"></MyEditor>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
<el-form-item label="状态" prop="status" :rules="[{ required: true, message: '请选择状态', trigger: ['change'] }]">
<el-select v-model="form.status" placeholder="请选择状态" class="w100" @change="onStatusChange">
<el-option v-for="item in state.msgStatusList" :key="item.label" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" v-if="isScheduledStatus">
<el-form-item
label="发布时间"
prop="publishTime"
:rules="[{
required: isScheduledStatus,
message: '请选择发布时间',
trigger: ['change', 'blur']
}]"
>
<el-date-picker
v-model="form.publishTime"
type="datetime"
placeholder="请选择发布时间"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
:disabled-date="disabledDate"
class="w100"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel"> </el-button>
<el-button type="primary" @click="onSure" :loading="state.sureLoading"> </el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup name="admin/msg/form">
import { reactive, toRefs, ref, defineAsyncComponent, getCurrentInstance, computed } from 'vue'
import { MsgUpdateInput, MsgTypeGetListOutput } from '/@/api/admin/data-contracts'
import { MsgApi } from '/@/api/admin/Msg'
import { cloneDeep } from 'lodash-es'
import eventBus from '/@/utils/mitt'
import { MsgTypeApi } from '/@/api/admin/MsgType'
import { listToTree } from '/@/utils/tree'
import { MsgStatusEnum } from '/@/api/admin/enum-contracts'
import { toOptionsByValue } from '/@/utils/enum'
const MyEditor = defineAsyncComponent(() => import('/@/components/my-editor/index.vue'))
// MsgUpdateInput
interface ExtendedMsgUpdateInput extends MsgUpdateInput {
publishTime?: string
}
defineProps({
title: {
type: String,
default: '',
},
})
const formRef = ref()
const editorRef = ref()
const state = reactive({
showDialog: false,
sureLoading: false,
form: { content: '' } as ExtendedMsgUpdateInput,
msgTypeTreeData: [] as MsgTypeGetListOutput[],
v: null,
msgStatusList: toOptionsByValue(MsgStatusEnum),
})
const { proxy } = getCurrentInstance() as any
const { form } = toRefs(state)
//
const isScheduledStatus = computed(() => {
return state.form.status === 3 // 3 Scheduled
})
const testEditorContent = (rule: any, value: any, callback: any) => {
if (!value) {
callback()
}
if (editorRef.value.isEmpty()) {
callback(new Error('请输入内容'))
} else {
callback()
}
}
const onValidateContent = () => {
formRef.value.validateField('content')
}
const getMsgTypes = async () => {
const res = await new MsgTypeApi().getList().catch(() => {
state.msgTypeTreeData = []
})
if (res?.success && res.data && res.data.length > 0) {
state.msgTypeTreeData = listToTree(res.data)
} else {
state.msgTypeTreeData = []
}
}
//
const onStatusChange = (value: number) => {
if (value !== 3) { // 3
state.form.publishTime = undefined
//
formRef.value?.clearValidate('publishTime')
}
}
//
const disabledDate = (time: Date) => {
return time.getTime() < Date.now() - 86400000 //
}
//
const open = async (row: ExtendedMsgUpdateInput = { id: 0 }) => {
proxy.$modal.loading()
await getMsgTypes()
let formData = cloneDeep(row) as ExtendedMsgUpdateInput
if (row.id > 0) {
const res = await new MsgApi().get({ id: row.id }).catch(() => {
proxy.$modal.closeLoading()
})
if (res?.success) {
formData = res.data as ExtendedMsgUpdateInput
formData.typeId = formData.typeId && formData.typeId > 0 ? formData.typeId : undefined
}
}
state.form = formData
state.form.typeId = 690214370156613
proxy.$modal.closeLoading()
state.showDialog = true
}
//
const onCancel = () => {
state.showDialog = false
}
//
const onSure = () => {
formRef.value.validate(async (valid: boolean) => {
if (!valid) return
//
if (isScheduledStatus.value) {
if (!state.form.publishTime) {
proxy.$message.error('选择定时发布时必须选择发布时间')
return
}
const publishTime = new Date(state.form.publishTime)
const now = new Date()
if (publishTime <= now) {
proxy.$message.error('发布时间必须晚于当前时间')
return
}
} else {
//
state.form.publishTime = undefined
}
state.sureLoading = true
let res = {} as any
if (state.form.id != undefined && state.form.id > 0) {
res = await new MsgApi().update(state.form, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
} else {
res = await new MsgApi().add(state.form, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
}
state.sureLoading = false
if (res?.success) {
eventBus.emit('refreshMsg')
state.showDialog = false
}
})
}
defineExpose({
open,
})
</script>
<style lang="scss" scoped></style>

View File

@ -1,319 +0,0 @@
<template>
<MySplitPanes>
<pane size="55" min-size="35" max-size="65">
<div class="my-flex-column w100 h100">
<el-card class="my-query-box mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
<el-form :inline="true" @submit.stop.prevent>
<el-form-item label="标题">
<el-input v-model="state.filter.msgName" placeholder="标题" @keyup.enter="onQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="ele-Search" @click="onQuery"> 查询 </el-button>
<el-button v-auth="'api:admin:msg:add'" type="primary" icon="ele-Plus" @click="onAdd"> 新增 </el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="my-fill mt8" shadow="never">
<el-table
ref="msgTableRef"
v-loading="state.loading"
:data="state.msgData"
default-expand-all
highlight-current-row
style="width: 100%"
@current-change="onTableCurrentChange"
border
>
<el-table-column prop="title" label="标题" min-width="120" show-overflow-tooltip />
<el-table-column prop="typeName" label="消息分类" min-width="120" show-overflow-tooltip />
<el-table-column prop="status" label="状态" min-width="90" show-overflow-tooltip :formatter="formatterMsgStatusEnum" />
<el-table-column prop="createdTime" label="创建时间" :formatter="formatterTime" min-width="160" show-overflow-tooltip />
<el-table-column label="操作" width="100" fixed="right" header-align="center" align="center">
<template #default="{ row }">
<my-dropdown-more v-auths="['api:admin:msg:update', 'api:admin:msg:delete']" style="margin-left: 0px">
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item v-if="auth('api:admin:msg:update')" @click="onEdit(row)">编辑</el-dropdown-item>
<el-dropdown-item v-if="auth('api:admin:msg:delete')" @click="onDelete(row)">删除</el-dropdown-item>
</el-dropdown-menu>
</template>
</my-dropdown-more>
</template>
</el-table-column>
</el-table>
<div class="my-flex my-flex-end" style="margin-top: 10px">
<el-pagination
v-model:currentPage="state.pageInput.currentPage"
v-model:page-size="state.pageInput.pageSize"
:total="state.total"
:page-sizes="[10, 20, 50, 100]"
background
@size-change="onSizeChange"
@current-change="onCurrentChange"
layout="total, sizes, prev, pager, next, jumper"
/>
</div>
</el-card>
</div>
</pane>
<pane>
<div class="my-flex-column w100 h100">
<el-card class="my-query-box mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
<el-form :inline="true" @submit.stop.prevent>
<el-form-item label="姓名">
<el-input v-model="state.filter.name" placeholder="姓名" @keyup.enter="onGetMsgUserList" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="ele-Search" @click="onGetMsgUserList"> 查询 </el-button>
<el-button v-auth="'api:admin:msg:add-msg-user'" type="primary" icon="ele-Plus" @click="onAddUser"> 添加 </el-button>
<el-button v-auth="'api:admin:msg:remove-msg-user'" type="danger" icon="ele-Delete" @click="onRemoveUser"> 移除 </el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="my-fill mt8" shadow="never">
<el-table
ref="userTableRef"
v-loading="state.userListLoading"
:data="state.userListData"
row-key="id"
style="width: 100%"
@row-click="onUserRowClick"
border
>
<el-table-column type="selection" width="55" />
<el-table-column prop="name" label="姓名" min-width="120" show-overflow-tooltip />
<el-table-column label="是否已读" width="100" align="center">
<template #default="{ row }">
<el-tag type="success" v-if="row.isRead">已读</el-tag>
<el-tag type="info" v-else>未读</el-tag>
</template>
</el-table-column>
<el-table-column prop="readTime" label="已读时间" :formatter="formatterTime" min-width="160" show-overflow-tooltip />
<!-- <el-table-column prop="mobile" label="手机号" min-width="120" show-overflow-tooltip />
<el-table-column prop="email" label="邮箱" min-width="180" show-overflow-tooltip /> -->
</el-table>
</el-card>
</div>
</pane>
<msg-form ref="msgFormRef" :title="state.msgFormTitle"></msg-form>
<user-select
ref="userSelectRef"
:title="`添加【${state.msgName}】员工`"
multiple
:sure-loading="state.sureLoading"
@sure="onSureUser"
></user-select>
</MySplitPanes>
</template>
<script lang="ts" setup name="admin/msg">
import { ref, reactive, onMounted, getCurrentInstance, onBeforeMount, defineAsyncComponent } from 'vue'
import {
PageInputMsgGetPageInput,
MsgGetMsgUserListOutput,
UserGetPageOutput,
MsgAddMsgUserListInput,
MsgGetPageOutput,
} from '/@/api/admin/data-contracts'
import { MsgApi } from '/@/api/admin/Msg'
import { ElTable } from 'element-plus'
import eventBus from '/@/utils/mitt'
import { auth } from '/@/utils/authFunction'
import { Pane } from 'splitpanes'
import dayjs from 'dayjs'
import { MsgStatusEnum } from '/@/api/admin/enum-contracts'
import { getDescByValue } from '/@/utils/enum'
//
const MsgForm = defineAsyncComponent(() => import('./components/msg-form.vue'))
const UserSelect = defineAsyncComponent(() => import('/@/views/admin/user/components/user-select.vue'))
const MySplitPanes = defineAsyncComponent(() => import('/@/components/my-layout/split-panes.vue'))
const MyDropdownMore = defineAsyncComponent(() => import('/@/components/my-dropdown-more/index.vue'))
const { proxy } = getCurrentInstance() as any
const msgTableRef = ref()
const msgFormRef = ref()
const userTableRef = ref<InstanceType<typeof ElTable>>()
const userSelectRef = ref()
const state = reactive({
loading: false,
userListLoading: false,
sureLoading: false,
msgFormTitle: '',
filter: {
name: '',
msgName: '',
},
total: 0,
pageInput: {
currentPage: 1,
pageSize: 20,
filter: {
title: '',
msgTypeValue: 690214305554501,
},
} as PageInputMsgGetPageInput,
msgData: [] as any,
userListData: [] as MsgGetMsgUserListOutput[],
msgId: undefined as number | undefined,
msgName: '' as string | null | undefined,
})
onMounted(async () => {
onQuery()
eventBus.off('refreshMsg')
eventBus.on('refreshMsg', () => {
onQuery()
})
})
onBeforeMount(() => {
eventBus.off('refreshMsg')
})
const formatterMsgStatusEnum = (row: any, column: any, cellValue: any) => {
return getDescByValue(MsgStatusEnum, cellValue)
}
const formatterTime = (row: any, column: any, cellValue: any) => {
return cellValue ? dayjs(cellValue).format('YYYY-MM-DD HH:mm:ss') : ''
}
const onQuery = async () => {
state.loading = true
if (state.pageInput.filter) state.pageInput.filter.title = state.filter.msgName
const res = await new MsgApi().getPage(state.pageInput).catch(() => {
state.loading = false
})
state.msgData = res?.data?.list ?? []
state.total = res?.data?.total ?? 0
if (state.msgData?.length > 0) {
window.setTimeout(() => {
msgTableRef.value?.setCurrentRow(state.msgData[0])
}, 100)
// nextTick(() => {
// msgTableRef.value?.setCurrentRow(state.msgData[0])
// })
}
state.loading = false
}
const onSizeChange = (val: number) => {
state.pageInput.currentPage = 1
state.pageInput.pageSize = val
onQuery()
}
const onCurrentChange = (val: number) => {
state.pageInput.currentPage = val
onQuery()
}
const onAdd = () => {
state.msgFormTitle = '新增制度消息'
msgFormRef.value.open({ enabled: true })
}
const onEdit = (row: MsgGetPageOutput) => {
state.msgFormTitle = '编辑制度消息'
msgFormRef.value.open(row)
}
const onDelete = (row: MsgGetPageOutput) => {
proxy.$modal
.confirmDelete(`确定要删除消息【${row.title}】?`)
.then(async () => {
await new MsgApi().delete({ id: row.id }, { loading: true, showSuccessMessage: true })
onQuery()
})
.catch(() => {})
}
const onGetMsgUserList = async () => {
state.userListLoading = true
const res = await new MsgApi().getMsgUserList({ MsgId: state.msgId, Name: state.filter.name }).catch(() => {
state.userListLoading = false
})
state.userListLoading = false
if (res?.success) {
if (res.data && res.data.length > 0) {
state.userListData = res.data
} else {
state.userListData = []
}
}
}
const onTableCurrentChange = (currentRow: MsgGetPageOutput) => {
if (!currentRow) {
return
}
state.msgId = currentRow.id
state.msgName = currentRow.title
onGetMsgUserList()
}
const onUserRowClick = (row: MsgGetMsgUserListOutput) => {
userTableRef.value!.toggleRowSelection(row, undefined)
}
const onAddUser = () => {
if (!((state.msgId as number) > 0)) {
proxy.$modal.msgWarning('请选择消息')
return
}
userSelectRef.value.open({ msgId: state.msgId })
}
const onRemoveUser = () => {
if (!((state.msgId as number) > 0)) {
proxy.$modal.msgWarning('请选择消息')
return
}
const selectionRows = userTableRef.value!.getSelectionRows() as UserGetPageOutput[]
if (!((selectionRows.length as number) > 0)) {
proxy.$modal.msgWarning('请选择员工')
return
}
proxy.$modal
.confirm(`确定要移除吗?`)
.then(async () => {
const userIds = selectionRows?.map((a) => a.id)
const input = { msgId: state.msgId, userIds } as MsgAddMsgUserListInput
await new MsgApi().removeMsgUser(input, { loading: true })
onGetMsgUserList()
})
.catch(() => {})
}
const onSureUser = async (users: UserGetPageOutput[]) => {
if (!(users?.length > 0)) {
userSelectRef.value.close()
return
}
state.sureLoading = true
const userIds = users?.map((a) => a.id)
const input = { msgId: state.msgId, userIds } as MsgAddMsgUserListInput
await new MsgApi().addMsgUser(input, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
state.sureLoading = false
userSelectRef.value.close()
onGetMsgUserList()
}
</script>
<style scoped lang="scss"></style>

View File

@ -1,188 +0,0 @@
<template>
<div>
<el-dialog
v-model="state.showDialog"
destroy-on-close
:title="title"
draggable
:close-on-click-modal="false"
:close-on-press-escape="false"
width="600px"
>
<el-form :model="form" ref="formRef" label-width="110px">
<el-row :gutter="35">
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="网站名称" prop="websiteName" :rules="[{ required: true, message: '请输入网站名称', trigger: ['blur', 'change'] }]">
<el-input v-model="form.websiteName" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="网站地址" prop="websiteUrl" :rules="[{ required: true, message: '请输入网站地址', trigger: ['blur', 'change'] }]">
<el-input v-model="form.websiteUrl" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="适用Site" prop="applicableSite" :rules="[{ required: true, message: '请选择适用Site', trigger: ['blur', 'change'] }]">
<el-select v-model="form.applicableSite" placeholder="请选择适用Site" clearable class="w100">
<el-option v-for="item in state.Site" :key="item.code" :label="item.name" :value="item.code" />
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="一级标题" prop="primaryTitle" :rules="[{ required: true, message: '请选择一级标题', trigger: ['blur', 'change'] }]">
<el-select v-model="form.primaryTitle" placeholder="请选择一级标题" clearable class="w100">
<el-option v-for="item in state.OnlineWebOne" :key="item.code" :label="item.name" :value="item.code" />
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="二级标题" prop="secondaryTitle" :rules="[{ required: true, message: '请选择二级标题', trigger: ['blur', 'change'] }]">
<el-select v-model="form.secondaryTitle" placeholder="请选择二级标题" clearable class="w100">
<el-option v-for="item in state.OnlineWebTwo" :key="item.code" :label="item.name" :value="item.code" />
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="联系人" prop="contactPerson">
<el-input v-model="form.contactPerson" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="教程地址" prop="tutorialUrl">
<el-input v-model="form.tutorialUrl" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="功能介绍" prop="functionIntro" :rules="[{ required: true, message: '请输入功能介绍', trigger: ['blur', 'change'] }]">
<el-input v-model="form.functionIntro" type="textarea" rows="3" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="关键词" prop="keywords" :rules="[{ required: true, message: '请输入关键词', trigger: ['blur', 'change'] }]">
<el-input v-model="form.keywords" clearable />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel"> </el-button>
<el-button type="primary" @click="onSure" :loading="state.sureLoading"> </el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup name="admin/online/form">
import { reactive, toRefs, ref } from 'vue'
import { OnlineUpdateInput, DictGetListOutput } from '/@/api/admin/data-contracts'
import { OnlineApi } from '/@/api/admin/Online'
import { DictApi } from '/@/api/admin/Dict'
import { cloneDeep } from 'lodash-es'
import eventBus from '/@/utils/mitt'
/** 字典分类 */
/** 字典分类 */
const DictType = {
OnlineWebOne: { dictTypeCode: 'OnlineWebOne', desc: '一级标题' },
OnlineWebTwo: { dictTypeCode: 'OnlineWebTwo', desc: '二级标题' },
Site: { dictTypeCode: 'Site', desc: '适用站点' },
}
defineProps({
title: {
type: String,
default: '',
},
})
const formRef = ref()
const state = reactive({
showDialog: false,
sureLoading: false,
OnlineWebOne: [] as DictGetListOutput[] | null,
OnlineWebTwo: [] as DictGetListOutput[] | null,
Site: [] as DictGetListOutput[] | null,
form: {} as OnlineUpdateInput,
})
const { form } = toRefs(state)
//
const getDictList = async () => {
const res = await new DictApi().getList([DictType.OnlineWebOne.dictTypeCode, DictType.OnlineWebTwo.dictTypeCode, DictType.Site.dictTypeCode]).catch(() => {})
if (res?.success && res.data) {
state.OnlineWebOne = res.data.onlineWebOne
state.OnlineWebTwo = res.data.onlineWebTwo
state.Site = res.data.site
}
console.log("🔍 ~ getDictList() callback ~ src/views/admin/online/index.vue:221 ~ getDictList():", state.OnlineWebOne, state.OnlineWebTwo, state.Site)
}
//
const open = async (row: Partial<OnlineUpdateInput> = { id: 0 }) => {
await getDictList()
let formData = cloneDeep(row) as OnlineUpdateInput
if (row.id && row.id > 0) {
const res = await new OnlineApi().get({ id: row.id })
if (res?.success) {
formData = res.data as OnlineUpdateInput
}
} else {
formData = {
id: 0,
websiteName: '',
primaryTitle: '',
secondaryTitle: '',
contactPerson: '',
tutorialUrl: '',
applicableSite: '',
functionIntro: '',
keywords: '',
websiteUrl: '',
}
}
state.form = formData
state.showDialog = true
}
//
const onCancel = () => {
state.showDialog = false
}
//
const onSure = () => {
formRef.value.validate(async (valid: boolean) => {
if (!valid) return
state.sureLoading = true
let res = {} as any
if (state.form.id != undefined && state.form.id > 0) {
res = await new OnlineApi().update(state.form).catch(() => {
state.sureLoading = false
})
} else {
res = await new OnlineApi().add(state.form).catch(() => {
state.sureLoading = false
})
}
state.sureLoading = false
if (res?.success === true) {
eventBus.emit('refreshOnline')
state.showDialog = false
}
})
}
defineExpose({
open,
})
</script>

View File

@ -1,215 +0,0 @@
<template>
<my-layout>
<div class="my-query-box mt8" style="position: relative">
<el-card shadow="never" :body-style="{ paddingBottom: '0' }">
<el-form :inline="true" label-width="auto" :label-position="'left'" @submit.stop.prevent>
<el-form-item label="关键词">
<el-input v-model="state.filter.websiteName" placeholder="请输入网站名称、功能简介、关键词、网址" style="width: 350px" @keyup.enter="onQuery" />
</el-form-item>
<el-form-item label="一级标题">
<el-select v-model="state.filter.primaryTitle" placeholder="请选择一级标题" clearable style="width: 150px" @change="onQuery">
<el-option v-for="item in state.OnlineWebOne" :key="item.code" :label="item.name" :value="item.code" />
</el-select>
</el-form-item>
<el-form-item label="二级标题">
<el-select v-model="state.filter.secondaryTitle" placeholder="请选择二级标题" clearable style="width: 150px" @change="onQuery">
<el-option v-for="item in state.OnlineWebTwo" :key="item.code" :label="item.name" :value="item.code" />
</el-select>
</el-form-item>
<el-form-item label="创建日期">
<MyDateRange v-model:startDate="state.filter.startCreatedTime" v-model:endDate="state.filter.endCreatedTime" style="width: 230px" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="ele-Search" @click="onQuery"> 查询 </el-button>
<el-button v-auth="'api:admin:online:add'" type="primary" icon="ele-Plus" @click="onAdd"> 新增 </el-button>
</el-form-item>
</el-form>
</el-card>
</div>
<el-card class="my-fill mt8 el-card-table" shadow="never">
<el-table ref="tableRef" v-loading="state.loading" :data="state.onlineListData" row-key="id" style="width: 100%" border>
<el-table-column type="index" label="序号" width="60" :index="indexMethod" />
<el-table-column prop="websiteName" label="网站名称" min-width="120" />
<el-table-column prop="primaryTitle" label="主标题" min-width="120" />
<el-table-column prop="secondaryTitle" label="副标题" min-width="120" />
<el-table-column prop="contactPerson" label="联系人" width="100" />
<el-table-column prop="tutorialUrl" label="教程地址" min-width="200">
<template #default="{ row }">
<el-link type="primary" :href="row.tutorialUrl" target="_blank">{{ row.tutorialUrl }}</el-link>
</template>
</el-table-column>
<el-table-column prop="websiteUrl" label="网站地址" min-width="200">
<template #default="{ row }">
<el-link type="primary" :href="row.websiteUrl" target="_blank">{{ row.websiteUrl }}</el-link>
</template>
</el-table-column>
<el-table-column prop="createdTime" label="创建时间" :formatter="formatterTime" width="100" />
<el-table-column label="操作" width="150" fixed="right" header-align="center" align="center">
<template #default="{ row }">
<el-button v-auth="'api:admin:online:update'" icon="ele-Edit" text type="primary" @click="onUpdate(row)">修改</el-button>
<el-button v-auth="'api:admin:online:delete'" icon="ele-Delete" text type="danger" @click="onDelete(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="my-flex my-flex-end" style="margin-top: 10px">
<el-pagination
v-model:currentPage="state.pageInput.currentPage"
v-model:page-size="state.pageInput.pageSize"
:total="state.total"
:page-sizes="[10, 20, 50, 100]"
background
@size-change="onSizeChange"
@current-change="onCurrentChange"
layout="total, sizes, prev, pager, next, jumper"
/>
</div>
</el-card>
<online-form ref="onlineFormRef" :title="state.onlineFormTitle"></online-form>
</my-layout>
</template>
<script lang="ts" setup name="admin/online">
import { ref, reactive, onMounted, onBeforeMount, getCurrentInstance, defineAsyncComponent } from 'vue'
import { ElTable, ElMessageBox, ElMessage } from 'element-plus'
import { OnlineGetOutput, PageInputOnlineGetPageInput, DictGetListOutput } from '/@/api/admin/data-contracts'
import { OnlineApi } from '/@/api/admin/Online'
import { DictApi } from '/@/api/admin/Dict'
import dayjs from 'dayjs'
import eventBus from '/@/utils/mitt'
import { cloneDeep } from 'lodash-es'
//
const OnlineForm = defineAsyncComponent(() => import('./components/online-form.vue'))
const MyDateRange = defineAsyncComponent(() => import('/@/components/my-date-range/index.vue'))
const { proxy } = getCurrentInstance() as any
const onlineFormRef = ref()
const tableRef = ref<InstanceType<typeof ElTable>>()
/** 字典分类 */
const DictType = {
OnlineWebOne: { dictTypeCode: 'OnlineWebOne', desc: '一级标题' },
OnlineWebTwo: { dictTypeCode: 'OnlineWebTwo', desc: '二级标题' },
}
const state = reactive({
loading: false,
onlineFormTitle: '',
OnlineWebOne: [] as DictGetListOutput[] | null,
OnlineWebTwo: [] as DictGetListOutput[] | null,
filter: {
websiteName: '',
primaryTitle: '',
secondaryTitle: '',
startCreatedTime: undefined,
endCreatedTime: undefined,
},
total: 0,
pageInput: {
currentPage: 1,
pageSize: 20,
filter: {},
} as PageInputOnlineGetPageInput,
onlineListData: [] as Array<OnlineGetOutput>,
})
onMounted(() => {
getDictList()
onQuery()
eventBus.off('refreshOnline')
eventBus.on('refreshOnline', async () => {
onQuery()
})
})
onBeforeMount(() => {
eventBus.off('refreshOnline')
})
//
const formatterTime = (row: any, column: any, cellValue: any) => {
if (!cellValue) return ''
return dayjs(cellValue).format('YYYY-MM-DD')
}
//
const indexMethod = (index: number) => {
return ((state.pageInput.currentPage || 1) - 1) * (state.pageInput.pageSize || 20) + index + 1
}
//
const onQuery = async () => {
state.loading = true
const res = await new OnlineApi()
.getPage({
...state.pageInput,
filter: state.filter,
})
.catch(() => {
state.loading = false
})
state.loading = false
if (res?.success) {
state.onlineListData = res.data?.list ?? []
state.total = res.data?.total ?? 0
}
}
//
const onAdd = () => {
state.onlineFormTitle = '新增'
onlineFormRef.value.open()
}
//
const onUpdate = (row: OnlineGetOutput) => {
state.onlineFormTitle = '修改'
onlineFormRef.value.open(row)
}
//
const onDelete = async (row: OnlineGetOutput) => {
ElMessageBox.confirm(`确定删除在线教程:【${row.websiteName}】?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(async () => {
const res = await new OnlineApi().delete({ id: row.id })
if (res?.status === 200) {
ElMessage.success('删除成功')
onQuery()
}
})
.catch(() => {})
}
//
const onSizeChange = (val: number) => {
state.pageInput.pageSize = val
onQuery()
}
//
const onCurrentChange = (val: number) => {
state.pageInput.currentPage = val
onQuery()
}
//
const getDictList = async () => {
const res = await new DictApi().getList([DictType.OnlineWebOne.dictTypeCode, DictType.OnlineWebTwo.dictTypeCode]).catch(() => {})
if (res?.success && res.data) {
state.OnlineWebOne = res.data.onlineWebOne
state.OnlineWebTwo = res.data.onlineWebTwo
}
console.log("🔍 ~ getDictList() callback ~ src/views/admin/online/index.vue:221 ~ getDictList():", state.OnlineWebOne, state.OnlineWebTwo)
}
</script>

View File

@ -1,167 +0,0 @@
<template>
<div>
<el-dialog
v-model="state.showDialog"
destroy-on-close
:title="title"
draggable
:close-on-click-modal="false"
:close-on-press-escape="false"
width="600px"
>
<el-form :model="form" ref="formRef" label-width="80px">
<el-row :gutter="35">
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="上级部门" prop="parentId" :rules="[{ required: true, message: '请输入上级部门', trigger: ['change'] }]">
<el-tree-select
v-model="form.parentId"
:data="state.data"
node-key="id"
:props="{ label: 'name' }"
check-strictly
default-expand-all
render-after-expand
fit-input-width
clearable
class="w100"
/>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="部门名称" prop="name" :rules="[{ required: true, message: '请输入部门名称', trigger: ['blur', 'change'] }]">
<el-input v-model="form.name" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="部门编码" prop="code">
<el-input v-model="form.code" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="部门值" prop="value">
<el-input v-model="form.value" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="排序">
<el-input-number v-model="form.sort" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="启用">
<el-switch v-model="form.enabled" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="说明">
<el-input v-model="form.description" clearable type="textarea" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel"> </el-button>
<el-button type="primary" @click="onSure" :loading="state.sureLoading"> </el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup name="admin/org/form">
import { reactive, toRefs, ref, getCurrentInstance } from 'vue'
import { OrgUpdateInput } from '/@/api/admin/data-contracts'
import { OrgApi } from '/@/api/admin/Org'
import eventBus from '/@/utils/mitt'
import { listToTree } from '/@/utils/tree'
const { proxy } = getCurrentInstance() as any
defineProps({
title: {
type: String,
default: '',
},
})
const formRef = ref()
const state = reactive({
showDialog: false,
sureLoading: false,
form: {
enabled: true,
} as OrgUpdateInput,
data: [],
})
const { form } = toRefs(state)
const query = async () => {
const res = await new OrgApi().getList().catch(() => {})
if (res && res.data && res.data.length > 0) {
state.data = listToTree(res.data)
} else {
state.data = []
}
}
//
const open = async (row: any = {}) => {
proxy.$modal.loading()
await query()
proxy.$modal.closeLoading()
if (row.id > 0) {
const res = await new OrgApi().get({ id: row.id }, { loading: true })
if (res?.success) {
let formData = res.data as OrgUpdateInput
formData.parentId = formData.parentId && formData.parentId > 0 ? formData.parentId : undefined
state.form = formData
}
} else {
state.form = {
enabled: true,
parentId: row.parentId > 0 ? row.parentId : undefined,
} as OrgUpdateInput
}
state.showDialog = true
}
//
const onCancel = () => {
state.showDialog = false
}
//
const onSure = () => {
formRef.value.validate(async (valid: boolean) => {
if (!valid) return
state.sureLoading = true
let res = {} as any
state.form.parentId = state.form.parentId && state.form.parentId > 0 ? state.form.parentId : undefined
if (state.form.id != undefined && state.form.id > 0) {
res = await new OrgApi().update(state.form, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
} else {
res = await new OrgApi().add(state.form, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
}
state.sureLoading = false
if (res?.success) {
eventBus.emit('refreshOrg')
eventBus.emit('refreshOrgImg')
state.showDialog = false
}
})
}
defineExpose({
open,
})
</script>

View File

@ -1,208 +0,0 @@
<template>
<div v-loading="state.loading" class="h100">
<vue3-tree-org
ref="orgRef"
:data="state.data"
:props="{
id: 'id',
pid: 'parentId',
label: 'name',
expand: 'expand',
children: 'children',
}"
center
:horizontal="false"
:collapsable="false"
:only-one-node="false"
:clone-node-drag="false"
:node-draggable="false"
:define-menus="[
{ name: '复制文本', command: 'copy' },
{ name: '新增部门', command: 'onAdd' },
{ name: '编辑部门', command: 'onEdit' },
{ name: '删除部门', command: 'onDelete' },
]"
:label-style="state.style"
:filter-node-method="filterNodeMethod"
@on-node-dblclick="onNodeDblclick"
@on-contextmenu="onContextmenu"
v-bind="$attrs"
>
<template v-if="state.showOrgCount" v-slot:expand="{ node }">
<div>{{ node.children.length }}</div>
</template>
</vue3-tree-org>
<org-form ref="orgFormRef" :title="state.orgFormTitle"></org-form>
</div>
</template>
<script lang="ts" setup name="admin/org-tree-img">
import { ref, reactive, onMounted, getCurrentInstance, onBeforeMount, defineAsyncComponent } from 'vue'
import { OrgGetListOutput } from '/@/api/admin/data-contracts'
import { OrgApi } from '/@/api/admin/Org'
import { listToTree } from '/@/utils/tree'
import eventBus from '/@/utils/mitt'
//
const OrgForm = defineAsyncComponent(() => import('./org-form.vue'))
const { proxy } = getCurrentInstance() as any
const orgRef = ref()
const orgFormRef = ref()
const state = reactive({
loading: false,
orgFormTitle: '',
filter: {
name: '',
},
style: {
background: '#fff',
color: '#5e6d82',
},
showOrgCount: true,
data: [] as any,
orgTreeData: [] as Array<OrgGetListOutput>,
})
onMounted(() => {
onQuery()
eventBus.off('refreshOrgImg')
eventBus.on('refreshOrgImg', () => {
onQuery()
})
})
onBeforeMount(() => {
eventBus.off('refreshOrgImg')
})
const onQuery = async () => {
state.loading = true
const res = await new OrgApi().getList().catch(() => {
state.loading = false
})
if (res && res.data && res.data.length > 0) {
const data = listToTree(res.data, {
extraData: {
expand: true,
},
})
state.orgTreeData = data
state.data = data?.length > 0 ? data[0] : {}
state.data['disabled'] = true
} else {
state.data = []
}
state.loading = false
}
const onAdd = (row: OrgGetListOutput) => {
state.orgFormTitle = '新增部门'
orgFormRef.value.open({ parentId: row?.id })
}
const onEdit = (row: OrgGetListOutput) => {
state.orgFormTitle = '编辑部门'
orgFormRef.value.open(row)
}
const onDelete = (row: OrgGetListOutput) => {
proxy.$modal
.confirmDelete(`确定要删除部门【${row.name}】?`)
.then(async () => {
await new OrgApi().delete({ id: row.id }, { loading: true })
onQuery()
})
.catch(() => {})
}
const onContextmenu = ({ node, command }: any) => {
switch (command) {
case 'onAdd':
onAdd(node.$$data)
break
case 'onEdit':
onEdit(node.$$data)
break
case 'onDelete':
onDelete(node.$$data)
break
}
}
const filter = (filterword: string) => {
orgRef.value.filter(filterword)
}
const filterNodeMethod = (value: string, data: any) => {
if (!value) return true
return data.label.indexOf(value) !== -1
}
const onNodeDblclick = (e: any, data: any) => {}
defineExpose({
filter,
})
</script>
<style lang="scss">
.zm-tree-contextmenu {
background-color: var(--el-bg-color-overlay);
border-color: var(--el-border-color);
li:hover {
background-color: var(--el-color-primary-light-9);
color: var(--el-color-primary);
}
}
</style>
<style scoped lang="scss">
:deep() {
.icon-fullscreen:before {
content: '\e603' !important;
}
.tree-org {
margin-top: 5px;
}
.zm-tree-org {
background-color: var(--el-bg-color-overlay);
padding: 0px;
}
.tree-org-node__inner {
background-color: var(--el-bg-color) !important;
color: var(--el-text-color-primary) !important;
border: 1px solid var(--el-border-color);
}
.tree-org-node:not(:first-child):before,
.tree-org-node:not(:last-child):after,
.tree-org-node:after,
.tree-org-node__children:before {
border-color: var(--el-border-color);
}
.zm-tree-handle .zm-tree-handle-item {
background-color: var(--el-bg-color-overlay);
color: var(--el-text-color-primary);
border-color: var(--el-border-color);
.zm-tree-restore {
border-color: var(--el-text-color-primary);
}
.zm-tree-restore:after {
border-top-color: var(--el-text-color-primary);
border-right-color: var(--el-text-color-primary);
}
&:hover {
background-color: var(--el-color-primary-light-9);
color: var(--el-color-primary);
border-color: var(--el-color-primary-light-7);
.zm-tree-restore,
.zm-tree-restore:after {
border-color: var(--el-color-primary);
}
}
}
}
</style>

View File

@ -1,150 +0,0 @@
<template>
<el-card shadow="never" style="margin-top: 8px" body-style="padding:0px;" class="my-fill">
<template #header>
<div class="my-flex">
<el-input v-model="state.filterText" placeholder="筛选部门" clearable />
<el-dropdown trigger="hover">
<div class="my-flex my-flex-items-center my-icon-more">
<my-icon name="more" color="var(--color)" size="22"></my-icon>
</div>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="expandAllNodes(true)">展开全部</el-dropdown-item>
<el-dropdown-item @click="expandAllNodes(false)">收缩全部</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</template>
<el-scrollbar v-loading="state.loading" height="100%" max-height="100%" :always="false" wrap-style="padding:10px">
<el-tree
ref="orgMenuRef"
:data="state.orgTreeData"
node-key="id"
:props="{ children: 'children', label: 'name' }"
:filter-node-method="onFilterNode"
highlight-current
check-strictly
default-expand-all
render-after-expand
:expand-on-click-node="false"
v-bind="$attrs"
@node-click="onNodeClick"
@check-change="onCheckChange"
/>
</el-scrollbar>
</el-card>
</template>
<script lang="ts" setup name="admin/org/menu">
import { onMounted, reactive, ref, watch, nextTick, PropType } from 'vue'
import { OrgGetListOutput } from '/@/api/admin/data-contracts'
import { OrgApi } from '/@/api/admin/Org'
import { listToTree } from '/@/utils/tree'
import { ElTree } from 'element-plus'
const props = defineProps({
modelValue: {
type: Array as PropType<number[] | undefined | null>,
default: () => [],
},
selectFirstNode: {
type: Boolean,
default: () => false,
},
})
const orgMenuRef = ref<InstanceType<typeof ElTree>>()
const state = reactive({
loading: false,
filterText: '',
orgTreeData: [] as Array<OrgGetListOutput>,
lastKey: 0,
})
watch(
() => state.filterText,
(val) => {
orgMenuRef.value?.filter(val)
}
)
onMounted(() => {
initData()
})
const emits = defineEmits<{
(e: 'node-click', node: OrgGetListOutput | null): void
(e: 'update:modelValue', node: any[] | undefined | null): void
}>()
/**
* 展开或收缩所有树节点
* @param expanded 是否展开
*/
const expandAllNodes = (expanded: boolean) => {
if (!orgMenuRef.value) return
const treeNodes = orgMenuRef.value.store.nodesMap
Object.values(treeNodes).forEach((node) => {
node.expanded = expanded
})
}
const onFilterNode = (value: string, data: OrgGetListOutput) => {
if (!value) return true
return data.name?.indexOf(value) !== -1
}
const onNodeClick = (node: OrgGetListOutput) => {
if (state.lastKey === node.id) {
state.lastKey = 0
orgMenuRef.value?.setCurrentKey(undefined)
emits('node-click', null)
} else {
state.lastKey = node.id as number
emits('node-click', node)
}
}
const onCheckChange = () => {
emits('update:modelValue', orgMenuRef.value?.getCheckedKeys())
}
const initData = async () => {
state.loading = true
const res = await new OrgApi().getList().catch(() => {
state.loading = false
})
state.loading = false
if (res?.success && res.data && res.data.length > 0) {
state.orgTreeData = listToTree(res.data)
if (state.orgTreeData.length > 0 && props.selectFirstNode) {
nextTick(() => {
const firstNode = state.orgTreeData[0]
orgMenuRef.value?.setCurrentKey(firstNode.id)
onNodeClick(firstNode)
})
}
} else {
state.orgTreeData = []
}
}
defineExpose({
orgMenuRef,
})
</script>
<style lang="scss" scoped>
.my-icon-more {
cursor: pointer;
--el-button-text-color: var(--el-text-color-regular);
--el-button-hover-text-color: var(--el-color-primary);
color: var(--el-button-text-color);
fill: currentColor;
&:hover {
color: var(--el-button-hover-text-color);
}
}
</style>

View File

@ -1,165 +0,0 @@
<template>
<MyLayout>
<el-card v-show="state.showQuery" class="my-query-box mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
<el-form :inline="true" label-width="auto" @submit.stop.prevent>
<el-form-item label="部门名称">
<el-input v-model="state.filter.name" placeholder="部门名称" @keyup.enter="onQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="ele-Search" @click="onQuery"> 查询 </el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="my-fill mt8" shadow="never">
<div class="my-tools-box mb8 my-flex my-flex-between">
<div>
<el-button v-show="state.showOrgList" v-auth="'api:admin:org:add'" type="primary" icon="ele-Plus" @click="onAdd"> 新增 </el-button>
</div>
<div>
<el-tooltip effect="dark" :content="state.showQuery ? '隐藏查询' : '显示查询'" placement="top">
<el-button :icon="state.showQuery ? 'ele-ArrowUp' : 'ele-ArrowDown'" circle @click="state.showQuery = !state.showQuery" />
</el-tooltip>
<el-tooltip effect="dark" :content="state.showOrgList ? '部门图形' : '部门列表'" placement="top">
<el-button :icon="state.showOrgList ? 'ele-Share' : 'ele-Grid'" circle @click="onChangeOrgList" />
</el-tooltip>
</div>
</div>
<el-table
v-if="state.showOrgList"
:data="state.data"
style="width: 100%"
v-loading="state.loading"
row-key="id"
default-expand-all
border
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
>
<el-table-column prop="name" label="部门名称" min-width="120" show-overflow-tooltip />
<el-table-column prop="code" label="部门编码" min-width="120" show-overflow-tooltip />
<el-table-column prop="value" label="部门值" min-width="82" show-overflow-tooltip />
<el-table-column prop="sort" label="排序" width="82" align="center" show-overflow-tooltip />
<el-table-column label="状态" width="82" align="center">
<template #default="{ row }">
<el-tag type="success" v-if="row.enabled">启用</el-tag>
<el-tag type="danger" v-else>禁用</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="200" fixed="right" header-align="center" align="center">
<template #default="{ row }">
<el-button v-if="auth('api:admin:org:add')" icon="ele-Plus" text type="primary" @click="onAdd(row)"> 新增 </el-button>
<el-button v-if="auth('api:admin:org:update') && row.parentId > 0" icon="ele-EditPen" text type="primary" @click="onEdit(row)"
>编辑</el-button
>
<el-button v-if="auth('api:admin:org:delete') && row.parentId > 0" icon="ele-Delete" text type="danger" @click="onDelete(row)"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
<OrgImg ref="orgImgRef" v-else></OrgImg>
</el-card>
<org-form ref="orgFormRef" :title="state.orgFormTitle"></org-form>
</MyLayout>
</template>
<script lang="ts" setup name="admin/org">
import { ref, reactive, onMounted, getCurrentInstance, onBeforeMount, defineAsyncComponent } from 'vue'
import { OrgGetListOutput } from '/@/api/admin/data-contracts'
import { OrgApi } from '/@/api/admin/Org'
import { listToTree, filterList } from '/@/utils/tree'
import eventBus from '/@/utils/mitt'
import { auth } from '/@/utils/authFunction'
//
const OrgForm = defineAsyncComponent(() => import('./components/org-form.vue'))
const OrgImg = defineAsyncComponent(() => import('./components/org-img.vue'))
const { proxy } = getCurrentInstance() as any
const orgFormRef = ref()
const orgImgRef = ref()
const state = reactive({
loading: false,
orgFormTitle: '',
filter: {
name: '',
},
data: [] as Array<OrgGetListOutput>,
showQuery: true,
showOrgList: true,
})
onMounted(() => {
Query()
eventBus.off('refreshOrg')
eventBus.on('refreshOrg', () => {
Query()
})
})
onBeforeMount(() => {
eventBus.off('refreshOrg')
})
const onChangeOrgList = () => {
state.showOrgList = !state.showOrgList
if (state.showOrgList) {
Query()
}
}
const onQuery = () => {
if (state.showOrgList) {
Query()
} else {
orgImgRef.value.filter(state.filter.name)
}
}
const Query = async () => {
state.loading = true
const res = await new OrgApi().getList().catch(() => {
state.loading = false
})
if (res && res.data && res.data.length > 0) {
state.data = listToTree(
state.filter.name
? filterList(res.data, state.filter.name, {
filterWhere: (item: any, filterword: string) => {
return item.name?.toLocaleLowerCase().indexOf(filterword) > -1
},
})
: res.data
)
} else {
state.data = []
}
state.loading = false
}
const onAdd = (row: OrgGetListOutput) => {
state.orgFormTitle = '新增部门'
orgFormRef.value.open({ parentId: row.id })
}
const onEdit = (row: OrgGetListOutput) => {
state.orgFormTitle = '编辑部门'
orgFormRef.value.open(row)
}
const onDelete = (row: OrgGetListOutput) => {
proxy.$modal
.confirmDelete(`确定要删除部门【${row.name}】?`)
.then(async () => {
await new OrgApi().delete({ id: row.id }, { loading: true })
Query()
})
.catch(() => {})
}
</script>
<style scoped lang="scss"></style>

View File

@ -1,257 +0,0 @@
<template>
<div>
<el-dialog
v-model="state.showDialog"
destroy-on-close
:title="title"
draggable
:close-on-click-modal="false"
:close-on-press-escape="false"
width="600px"
>
<el-form :model="form" ref="formRef" label-width="80px">
<el-row :gutter="35">
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="所属平台">
<el-select v-model="form.platform" disabled placeholder="请选择所属平台" class="w100">
<el-option v-for="item in state.dictData[DictType.PlatForm.name]" :key="item.code" :label="item.name" :value="item.code" />
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="上级菜单">
<el-tree-select
v-model="form.parentId"
:data="permissionTreeData"
node-key="id"
check-strictly
default-expand-all
render-after-expand
fit-input-width
clearable
filterable
class="w100"
/>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="API接口">
<el-tree-select
v-model="form.apiIds"
:data="state.apiTreeData"
node-key="id"
:props="{ label: 'path' }"
render-after-expand
fit-input-width
clearable
filterable
multiple
collapse-tags
collapse-tags-tooltip
:filter-node-method="onApiFilterNode"
class="w100"
:default-expanded-keys="state.expandRowKeys"
@current-change="onApiCurrentChange"
>
<template #default="{ data }">
<span class="my-flex my-flex-between">
<span>{{ data.label }}</span>
<span class="my-line-1 my-mlr-12" :title="data.path">
{{ data.path }}
</span>
</span>
</template>
</el-tree-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="名称" prop="label" :rules="[{ required: true, message: '请输入名称', trigger: ['blur', 'change'] }]">
<el-input v-model="form.label" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="编码" prop="code" :rules="[{ required: true, message: '请输入编码', trigger: ['blur', 'change'] }]">
<el-input v-model="form.code" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="排序">
<el-input-number v-model="form.sort" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="启用">
<el-switch v-model="form.enabled" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="说明">
<el-input v-model="form.description" clearable type="textarea" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel"> </el-button>
<el-button type="primary" @click="onSure" :loading="state.sureLoading"> </el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup name="admin/permission/permission-dot-form">
import { reactive, toRefs, getCurrentInstance, ref, PropType, markRaw } from 'vue'
import { PermissionGetListOutput, PermissionUpdateDotInput, ApiGetListOutput, DictGetListOutput } from '/@/api/admin/data-contracts'
import { PermissionApi } from '/@/api/admin/Permission'
import { ApiApi } from '/@/api/admin/Api'
import { listToTree, treeToList } from '/@/utils/tree'
import eventBus from '/@/utils/mitt'
import { trimStart, replace, cloneDeep } from 'lodash-es'
import { DictApi } from '/@/api/admin/Dict'
/** 字典分类 */
const DictType = {
PlatForm: { name: 'platform', desc: '平台' },
}
defineProps({
title: {
type: String,
default: '',
},
permissionTreeData: {
type: Array as PropType<PermissionGetListOutput[]>,
default: () => [],
},
})
const { proxy } = getCurrentInstance() as any
const formRef = ref()
const state = reactive({
showDialog: false,
sureLoading: false,
form: { enabled: true } as PermissionUpdateDotInput,
apiTreeData: [] as ApiGetListOutput[],
expandRowKeys: [] as number[],
dictData: {
[DictType.PlatForm.name]: [] as DictGetListOutput[] | null,
},
})
const { form } = toRefs(state)
const getDictList = async () => {
const res = await new DictApi().getList([DictType.PlatForm.name]).catch(() => {})
if (res?.success && res.data) {
state.dictData = markRaw(res.data)
}
}
const getApis = async () => {
const res = await new ApiApi().getList()
if (res?.success && res.data && res.data.length > 0) {
state.apiTreeData = listToTree(res.data) as ApiGetListOutput[]
} else {
state.apiTreeData = []
}
}
//
const open = async (
row: PermissionUpdateDotInput = {
id: 0,
enabled: true,
parentId: undefined,
},
isCopy = false
) => {
proxy.$modal.loading()
await getDictList()
await getApis()
state.expandRowKeys = treeToList(cloneDeep(state.apiTreeData))
.filter((a: ApiGetListOutput) => a.parentId === 0)
.map((a: ApiGetListOutput) => a.id) as number[]
if (row.id > 0) {
const res = await new PermissionApi().getDot({ id: row.id }).catch(() => {
proxy.$modal.closeLoading()
})
if (res?.success) {
let formData = res.data as PermissionUpdateDotInput
formData.platform = row.platform
formData.parentId = formData.parentId && formData.parentId > 0 ? formData.parentId : undefined
if (isCopy) formData.id = 0
state.form = formData
}
} else {
state.form = row
}
proxy.$modal.closeLoading()
state.showDialog = true
}
const onApiFilterNode = (value: string, data: ApiGetListOutput) => {
if (!value) return true
return data.label?.indexOf(value) !== -1 || data.path?.indexOf(value) !== -1
}
const onApiCurrentChange = (data: ApiGetListOutput) => {
if (data?.httpMethods) {
if (!state.form.label) {
state.form.label = data.label
}
if (!state.form.code) {
state.form.code = trimStart(replace(data.path || '', /\//g, ':'), ':')
}
}
}
//
const onCancel = () => {
state.showDialog = false
}
//
const onSure = () => {
formRef.value.validate(async (valid: boolean) => {
if (!valid) return
state.sureLoading = true
let res = {} as any
state.form.parentId = state.form.parentId && state.form.parentId > 0 ? state.form.parentId : undefined
if (state.form.id != undefined && state.form.id > 0) {
res = await new PermissionApi().updateDot(state.form, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
} else {
res = await new PermissionApi().addDot(state.form, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
}
state.sureLoading = false
if (res?.success) {
eventBus.emit('refreshPermission')
state.showDialog = false
}
})
}
defineExpose({
open,
})
</script>
<style scoped lang="scss">
.my-mlr-12 {
margin-left: 12px;
margin-right: 12px;
}
</style>

View File

@ -1,201 +0,0 @@
<template>
<div>
<el-dialog
v-model="state.showDialog"
destroy-on-close
:title="title"
draggable
:close-on-click-modal="false"
:close-on-press-escape="false"
width="600px"
>
<el-form :model="form" ref="formRef" label-width="80px">
<el-row :gutter="35">
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="所属平台">
<el-select v-model="form.platform" disabled placeholder="请选择所属平台" class="w100">
<el-option v-for="item in state.dictData[DictType.PlatForm.name]" :key="item.code" :label="item.name" :value="item.code" />
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="上级分组">
<el-tree-select
v-model="form.parentId"
:data="permissionTreeData"
node-key="id"
check-strictly
default-expand-all
render-after-expand
fit-input-width
clearable
:empty-values="[0, null, undefined]"
class="w100"
/>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="名称" prop="label" :rules="[{ required: true, message: '请输入名称', trigger: ['blur', 'change'] }]">
<el-input v-model="form.label" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="路由地址" prop="path" :rules="[{ required: true, message: '请输入路由地址', trigger: ['blur', 'change'] }]">
<el-input v-model="form.path" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="重定向">
<el-input v-model="form.redirect" clearable placeholder="重定向地址" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="图标" prop="icon">
<my-select-icon v-model="form.icon" clearable class="w100" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="排序">
<el-input-number v-model="form.sort" class="w100" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="启用">
<el-switch v-model="form.enabled" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="展开">
<el-switch v-model="form.opened" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="隐藏">
<el-switch v-model="form.hidden" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel"> </el-button>
<el-button type="primary" @click="onSure" :loading="state.sureLoading"> </el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup name="admin/permission/permission-group-form">
import { reactive, toRefs, getCurrentInstance, ref, PropType, defineAsyncComponent, markRaw } from 'vue'
import { PermissionGetListOutput, PermissionUpdateGroupInput, DictGetListOutput } from '/@/api/admin/data-contracts'
import { PermissionApi } from '/@/api/admin/Permission'
import eventBus from '/@/utils/mitt'
import { DictApi } from '/@/api/admin/Dict'
//
const MySelectIcon = defineAsyncComponent(() => import('/@/components/my-select-icon/index.vue'))
/** 字典分类 */
const DictType = {
PlatForm: { name: 'platform', desc: '平台' },
}
defineProps({
title: {
type: String,
default: '',
},
permissionTreeData: {
type: Array as PropType<PermissionGetListOutput[]>,
default: () => [],
},
})
const { proxy } = getCurrentInstance() as any
const formRef = ref()
const state = reactive({
showDialog: false,
sureLoading: false,
form: { enabled: true, opened: true } as PermissionUpdateGroupInput,
dictData: {
[DictType.PlatForm.name]: [] as DictGetListOutput[] | null,
},
})
const { form } = toRefs(state)
const getDictList = async () => {
const res = await new DictApi().getList([DictType.PlatForm.name]).catch(() => {})
if (res?.success && res.data) {
state.dictData = markRaw(res.data)
}
}
//
const open = async (
row: PermissionUpdateGroupInput = {
id: 0,
enabled: true,
opened: true,
icon: 'ele-Memo',
parentId: undefined,
},
isCopy = false
) => {
proxy.$modal.loading()
await getDictList()
if (row.id > 0) {
const res = await new PermissionApi().getGroup({ id: row.id }).catch(() => {
proxy.$modal.closeLoading()
})
if (res?.success) {
let formData = res.data as PermissionUpdateGroupInput
formData.platform = row.platform
formData.parentId = formData.parentId && formData.parentId > 0 ? formData.parentId : undefined
if (isCopy) formData.id = 0
state.form = formData
}
} else {
state.form = row
}
proxy.$modal.closeLoading()
state.showDialog = true
}
//
const onCancel = () => {
state.showDialog = false
}
//
const onSure = () => {
formRef.value.validate(async (valid: boolean) => {
if (!valid) return
state.sureLoading = true
let res = {} as any
state.form.parentId = state.form.parentId && state.form.parentId > 0 ? state.form.parentId : undefined
if (state.form.id != undefined && state.form.id > 0) {
res = await new PermissionApi().updateGroup(state.form, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
} else {
res = await new PermissionApi().addGroup(state.form, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
}
state.sureLoading = false
if (res?.success) {
eventBus.emit('refreshPermission')
state.showDialog = false
}
})
}
defineExpose({
open,
})
</script>

View File

@ -1,296 +0,0 @@
<template>
<div>
<el-dialog
v-model="state.showDialog"
destroy-on-close
:title="title"
draggable
:close-on-click-modal="false"
:close-on-press-escape="false"
width="600px"
>
<el-form :model="form" ref="formRef" label-width="80px">
<el-row :gutter="35">
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="所属平台">
<el-select v-model="form.platform" disabled placeholder="请选择所属平台" class="w100">
<el-option v-for="item in state.dictData[DictType.PlatForm.name]" :key="item.code" :label="item.name" :value="item.code" />
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="上级分组">
<el-tree-select
v-model="form.parentId"
:data="permissionTreeData"
node-key="id"
check-strictly
default-expand-all
render-after-expand
fit-input-width
clearable
class="w100"
/>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="视图">
<el-tree-select
v-model="form.viewId"
:data="state.viewTreeData"
node-key="id"
:props="{ label: 'path' }"
default-expand-all
render-after-expand
fit-input-width
clearable
filterable
:filter-node-method="onViewFilterNode"
class="w100"
@current-change="onViewCurrentChange"
>
<template #default="{ data }">
<span class="my-flex my-flex-between">
<span>{{ data.label }}</span>
<span class="my-line-1 my-ml-12" :title="data.path">
{{ data.path }}
</span>
</span>
</template>
</el-tree-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="名称" prop="label" :rules="[{ required: true, message: '请输入名称', trigger: ['blur', 'change'] }]">
<el-input v-model="form.label" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="路由地址" prop="path" :rules="[{ required: true, message: '请输入路由地址', trigger: ['blur', 'change'] }]">
<el-input v-model="form.path" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="路由命名">
<el-input v-model="form.name" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="链接地址">
<el-input v-model="form.link" clearable placeholder="内嵌/外链链接地址" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="图标" prop="icon">
<my-select-icon v-model="form.icon" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="24" :lg="24" :xl="24">
<el-form-item label="排序">
<el-input-number v-model="form.sort" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="系统">
<el-switch v-model="form.isSystem" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="启用">
<el-switch v-model="form.enabled" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="内嵌窗口">
<el-switch v-model="form.isIframe" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="缓存">
<el-switch v-model="form.isKeepAlive" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="固定">
<el-switch v-model="form.isAffix" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="隐藏">
<el-switch v-model="form.hidden" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="新窗口">
<el-switch v-model="form.newWindow" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="链接外显">
<el-switch v-model="form.external" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel"> </el-button>
<el-button type="primary" @click="onSure" :loading="state.sureLoading"> </el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup name="admin/permission/permission-menu-form">
import { reactive, toRefs, getCurrentInstance, ref, PropType, defineAsyncComponent, markRaw } from 'vue'
import { PermissionGetListOutput, PermissionUpdateMenuInput, ViewGetListOutput, DictGetListOutput } from '/@/api/admin/data-contracts'
import { PermissionApi } from '/@/api/admin/Permission'
import { ViewApi } from '/@/api/admin/View'
import { listToTree } from '/@/utils/tree'
import eventBus from '/@/utils/mitt'
import { DictApi } from '/@/api/admin/Dict'
//
const MySelectIcon = defineAsyncComponent(() => import('/@/components/my-select-icon/index.vue'))
/** 字典分类 */
const DictType = {
PlatForm: { name: 'platform', desc: '平台' },
}
defineProps({
title: {
type: String,
default: '',
},
permissionTreeData: {
type: Array as PropType<PermissionGetListOutput[]>,
default: () => [],
},
})
const { proxy } = getCurrentInstance() as any
const formRef = ref()
const state = reactive({
showDialog: false,
sureLoading: false,
form: { enabled: true, isKeepAlive: true } as PermissionUpdateMenuInput,
viewTreeData: [] as ViewGetListOutput[],
dictData: {
[DictType.PlatForm.name]: [] as DictGetListOutput[] | null,
},
})
const { form } = toRefs(state)
const getDictList = async () => {
const res = await new DictApi().getList([DictType.PlatForm.name]).catch(() => {})
if (res?.success && res.data) {
state.dictData = markRaw(res.data)
}
}
const getViews = async (platform: string) => {
const res = await new ViewApi().getList({ platform: platform })
if (res?.success && res.data && res.data.length > 0) {
state.viewTreeData = listToTree(res.data) as ViewGetListOutput[]
} else {
state.viewTreeData = []
}
}
//
const open = async (
row: PermissionUpdateMenuInput = {
id: 0,
enabled: true,
isKeepAlive: true,
icon: 'ele-Memo',
parentId: undefined,
},
isCopy = false
) => {
proxy.$modal.loading()
await getDictList()
await getViews(row.platform as string)
if (row.id > 0) {
const res = await new PermissionApi().getMenu({ id: row.id }).catch(() => {
proxy.$modal.closeLoading()
})
if (res?.success) {
let formData = res.data as PermissionUpdateMenuInput
formData.platform = row.platform
formData.parentId = formData.parentId && formData.parentId > 0 ? formData.parentId : undefined
if (isCopy) formData.id = 0
state.form = formData
}
} else {
state.form = row
}
proxy.$modal.closeLoading()
state.showDialog = true
}
const onViewFilterNode = (value: string, data: ViewGetListOutput) => {
if (!value) return true
return data.label?.indexOf(value) !== -1 || data.path?.indexOf(value) !== -1
}
const onViewCurrentChange = (data: ViewGetListOutput) => {
if (data) {
if (!state.form.label) {
state.form.label = data.label
}
if (!state.form.path) {
state.form.path = '/' + data.path
}
}
}
//
const onCancel = () => {
state.showDialog = false
}
//
const onSure = () => {
formRef.value.validate(async (valid: boolean) => {
if (!valid) return
state.sureLoading = true
let res = {} as any
state.form.parentId = state.form.parentId && state.form.parentId > 0 ? state.form.parentId : undefined
if (state.form.id != undefined && state.form.id > 0) {
res = await new PermissionApi().updateMenu(state.form, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
} else {
res = await new PermissionApi().addMenu(state.form, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
}
state.sureLoading = false
if (res?.success) {
eventBus.emit('refreshPermission')
state.showDialog = false
}
})
}
defineExpose({
open,
})
</script>
<style scoped lang="scss">
.my-ml-12 {
margin-left: 12px;
}
</style>

View File

@ -1,312 +0,0 @@
<template>
<my-layout>
<el-card class="my-query-box mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
<el-form :inline="true" @submit.stop.prevent>
<el-form-item label="平台">
<el-select v-model="state.filter.platform" placeholder="平台" :empty-values="[null]" @change="onQuery" style="width: 100px">
<el-option v-for="item in state.dictData[DictType.PlatForm.name]" :key="item.code" :label="item.name" :value="item.code" />
</el-select>
</el-form-item>
<el-form-item label="权限名称">
<el-input v-model="state.filter.label" placeholder="权限名称" @keyup.enter="onQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="ele-Search" @click="onQuery"> 查询 </el-button>
<el-dropdown
v-auths="['api:admin:permission:addgroup', 'api:admin:permission:addmenu', 'api:admin:permission:adddot']"
style="margin-left: 12px"
>
<el-button type="primary"
>新增<el-icon class="el-icon--right"><ele-ArrowDown /></el-icon
></el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item v-if="auth('api:admin:permission:addgroup')" @click="onAdd({ type: 1 })">新增分组</el-dropdown-item>
<el-dropdown-item v-if="auth('api:admin:permission:addmenu')" @click="onAdd({ type: 2 })">新增菜单</el-dropdown-item>
<el-dropdown-item v-if="auth('api:admin:permission:adddot')" @click="onAdd({ type: 3 })">新增权限点</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</el-form-item>
</el-form>
</el-card>
<el-card class="my-fill mt8" shadow="never">
<el-table
:data="state.permissionTreeData"
style="width: 100%"
v-loading="state.loading"
row-key="id"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
:expand-row-keys="state.expandRowKeys"
border
>
<el-table-column prop="label" label="权限名称" width="240" show-overflow-tooltip>
<template #default="{ row }">
<SvgIcon :name="row.icon" style="vertical-align: -2px" />
{{ row.label }}
</template>
</el-table-column>
<el-table-column prop="type" label="类型" width="82" show-overflow-tooltip>
<template #default="{ row }">
{{ row.type === 1 ? '分组' : row.type === 2 ? '菜单' : row.type === 3 ? '权限点' : '' }}
</template>
</el-table-column>
<el-table-column prop="path" label="权限地址" min-width="240" show-overflow-tooltip>
<template #default="{ row }">
<div v-if="row.type === 1 || row.type === 2">
{{ row.path ? '路由地址:' + row.path : '' }}
{{ row.viewPath ? '视图地址:' + row.viewPath : '' }}
{{ row.redirect ? '重定向地址:' + row.redirect : '' }}
{{ row.link ? '链接地址:' + row.link : '' }}
</div>
<div v-if="row.type === 3">接口地址{{ row.apiPaths }}</div>
</template>
</el-table-column>
<el-table-column prop="sort" label="排序" width="82" align="center" show-overflow-tooltip />
<el-table-column label="状态" width="82" align="center">
<template #default="{ row }">
<el-tag type="success" v-if="row.enabled">启用</el-tag>
<el-tag type="danger" v-else>禁用</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="160" fixed="right" header-align="center" align="center">
<template #default="{ row }">
<el-button
v-if="
(row.type === 1 && auth('api:admin:permission:updategroup')) ||
(row.type === 2 && auth('api:admin:permission:updatemenu')) ||
(row.type === 3 && auth('api:admin:permission:updatedot'))
"
icon="ele-EditPen"
text
type="primary"
@click="onEdit(row)"
>编辑</el-button
>
<my-dropdown-more
v-auths="[
'api:admin:permission:delete',
'api:admin:permission:addgroup',
'api:admin:permission:addmenu',
'api:admin:permission:adddot',
]"
>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item v-if="row.type === 1 && auth('api:admin:permission:addgroup')" @click="onAdd({ type: 1, parentId: row.id })">
新增分组
</el-dropdown-item>
<el-dropdown-item v-if="row.type === 1 && auth('api:admin:permission:addmenu')" @click="onAdd({ type: 2, parentId: row.id })">
新增菜单
</el-dropdown-item>
<el-dropdown-item v-if="row.type === 2 && auth('api:admin:permission:adddot')" @click="onAdd({ type: 3, parentId: row.id })">
新增权限点
</el-dropdown-item>
<el-dropdown-item v-if="auth('api:admin:permission:delete')" @click="onDelete(row)">删除</el-dropdown-item>
<el-dropdown-item v-if="row.type === 1 && auth('api:admin:permission:addgroup')" @click="onCopy(row)"> 复制 </el-dropdown-item>
<el-dropdown-item v-if="row.type === 2 && auth('api:admin:permission:addmenu')" @click="onCopy(row)"> 复制 </el-dropdown-item>
<el-dropdown-item v-if="row.type === 3 && auth('api:admin:permission:adddot')" @click="onCopy(row)"> 复制 </el-dropdown-item>
</el-dropdown-menu>
</template>
</my-dropdown-more>
</template>
</el-table-column>
</el-table>
</el-card>
<permission-group-form
ref="permissionGroupFormRef"
:title="state.permissionFormTitle"
:permission-tree-data="state.formPermissionGroupTreeData"
></permission-group-form>
<permission-menu-form
ref="permissionMenuFormRef"
:title="state.permissionFormTitle"
:permission-tree-data="state.formPermissionGroupTreeData"
></permission-menu-form>
<permission-dot-form
ref="permissionDotFormRef"
:title="state.permissionFormTitle"
:permission-tree-data="state.formPermissionMenuTreeData"
></permission-dot-form>
</my-layout>
</template>
<script lang="ts" setup name="admin/permission">
import { ref, reactive, onMounted, getCurrentInstance, onBeforeMount, defineAsyncComponent, markRaw } from 'vue'
import { PermissionGetListOutput, PermissionGetListInput, DictGetListOutput } from '/@/api/admin/data-contracts'
import { PermissionApi } from '/@/api/admin/Permission'
import { listToTree, treeToList, filterTree } from '/@/utils/tree'
import { cloneDeep } from 'lodash-es'
import eventBus from '/@/utils/mitt'
import { auth } from '/@/utils/authFunction'
import { DictApi } from '/@/api/admin/Dict'
import { PlatformType } from '/@/api/admin.extend/enum-contracts'
//
const PermissionGroupForm = defineAsyncComponent(() => import('./components/permission-group-form.vue'))
const PermissionMenuForm = defineAsyncComponent(() => import('./components/permission-menu-form.vue'))
const PermissionDotForm = defineAsyncComponent(() => import('./components/permission-dot-form.vue'))
const MyDropdownMore = defineAsyncComponent(() => import('/@/components/my-dropdown-more/index.vue'))
const { proxy } = getCurrentInstance() as any
const DictType = {
PlatForm: { name: 'platform', desc: '平台' },
}
const permissionGroupFormRef = ref()
const permissionMenuFormRef = ref()
const permissionDotFormRef = ref()
const state = reactive({
loading: false,
permissionFormTitle: '',
filter: {
platform: PlatformType.Web.name,
name: '',
} as PermissionGetListInput,
permissionTreeData: [] as Array<PermissionGetListOutput>,
formPermissionGroupTreeData: [] as Array<PermissionGetListOutput>,
formPermissionMenuTreeData: [] as Array<PermissionGetListOutput>,
expandRowKeys: [] as string[],
dictData: {
[DictType.PlatForm.name]: [] as DictGetListOutput[] | null,
},
})
onMounted(async () => {
await getDictList()
await onQuery()
state.expandRowKeys = treeToList(cloneDeep(state.permissionTreeData))
.filter((a: PermissionGetListOutput) => a.opened === true)
.map((a: PermissionGetListOutput) => a.id + '') as string[]
eventBus.off('refreshPermission')
eventBus.on('refreshPermission', async () => {
onQuery()
})
})
onBeforeMount(() => {
eventBus.off('refreshPermission')
})
const getDictList = async () => {
const res = await new DictApi().getList([DictType.PlatForm.name]).catch(() => {})
if (res?.success && res.data) {
state.dictData = markRaw(res.data)
}
}
const onQuery = async () => {
state.loading = true
const res = await new PermissionApi()
.getList({
platform: state.filter.platform,
})
.catch(() => {
state.loading = false
})
if (res && res.data && res.data.length > 0) {
const label = state.filter.label || ''
state.permissionTreeData = filterTree(listToTree(cloneDeep(res.data)), '', {
filterWhere: (item: any, keyword: string) => {
return item.label?.toLocaleLowerCase().indexOf(label) > -1 || item.path?.toLocaleLowerCase().indexOf(label) > -1
},
})
state.formPermissionGroupTreeData = listToTree(cloneDeep(res.data).filter((a: any) => a.type === 1))
state.formPermissionMenuTreeData = listToTree(cloneDeep(res.data).filter((a: any) => a.type === 1 || a.type === 2))
} else {
state.permissionTreeData = []
state.formPermissionGroupTreeData = []
state.formPermissionMenuTreeData = []
}
state.loading = false
}
const onAdd = (row: PermissionGetListOutput) => {
switch (row.type) {
case 1:
state.permissionFormTitle = '新增分组'
permissionGroupFormRef.value.open({
id: 0,
platform: state.filter.platform,
enabled: true,
opened: true,
icon: 'ele-Memo',
parentId: row.parentId,
})
break
case 2:
state.permissionFormTitle = '新增菜单'
permissionMenuFormRef.value.open({
id: 0,
platform: state.filter.platform,
enabled: true,
isKeepAlive: true,
icon: 'ele-Memo',
parentId: row.parentId,
})
break
case 3:
state.permissionFormTitle = '新增权限点'
permissionDotFormRef.value.open({
id: 0,
platform: state.filter.platform,
parentId: row.parentId,
enabled: true,
})
break
}
}
const onEdit = (row: PermissionGetListOutput) => {
row.platform = state.filter.platform
switch (row.type) {
case 1:
state.permissionFormTitle = '编辑分组'
permissionGroupFormRef.value.open(row)
break
case 2:
state.permissionFormTitle = '编辑菜单'
permissionMenuFormRef.value.open(row)
break
case 3:
state.permissionFormTitle = '编辑权限点'
permissionDotFormRef.value.open(row)
break
}
}
const onCopy = (row: PermissionGetListOutput) => {
switch (row.type) {
case 1:
state.permissionFormTitle = '新增分组'
permissionGroupFormRef.value.open(row, true)
break
case 2:
state.permissionFormTitle = '新增菜单'
permissionMenuFormRef.value.open(row, true)
break
case 3:
state.permissionFormTitle = '新增权限点'
permissionDotFormRef.value.open(row, true)
break
}
}
const onDelete = (row: PermissionGetListOutput) => {
proxy.$modal
.confirmDelete(`确定要删除${row.type === 1 ? '分组' : row.type === 2 ? '菜单' : row.type === 3 ? '权限点' : ''}${row.label}】?`)
.then(async () => {
await new PermissionApi().delete({ id: row.id }, { loading: true })
onQuery()
})
.catch(() => {})
}
</script>
<style scoped lang="scss"></style>

View File

@ -1,140 +0,0 @@
<template>
<div>
<el-dialog
v-model="state.showDialog"
destroy-on-close
:title="title"
draggable
:close-on-click-modal="false"
:close-on-press-escape="false"
width="475px"
>
<el-form ref="formRef" :model="form" label-width="80px" label-position="left">
<el-row :gutter="35">
<el-col :span="24">
<el-form-item label="旧密码" prop="oldPassword" :rules="[{ required: true, message: '请输入旧密码', trigger: ['blur', 'change'] }]">
<el-input v-model="form.oldPassword" show-password autocomplete="off" clearable />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item
label="新密码"
prop="newPassword"
:rules="[
{ required: true, message: '请输入新密码', trigger: ['blur', 'change'] },
{ validator: testNewPassword, trigger: ['blur', 'change'] },
{ validator: validatorPwd, trigger: ['blur', 'change'] },
]"
>
<el-input v-model="form.newPassword" show-password autocomplete="off" clearable @input="onInputNewPassword" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item
label="确认密码"
prop="confirmPassword"
:rules="[
{ required: true, message: '请输入确认密码', trigger: ['blur', 'change'] },
{ validator: testConfirmPassword, trigger: ['blur', 'change'] },
]"
>
<el-input v-model="form.confirmPassword" show-password autocomplete="off" clearable @input="onInputConfirmPassword" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel"> </el-button>
<el-button type="primary" @click="onSure" :loading="state.sureLoading"> </el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup name="admin/personal/change-password-form">
import { reactive, toRefs, ref } from 'vue'
import { UserChangePasswordInput } from '/@/api/admin/data-contracts'
import { UserApi } from '/@/api/admin/User'
import { verifyCnAndSpace } from '/@/utils/toolsValidate'
import { validatorPwd } from '/@/utils/validators'
defineProps({
title: {
type: String,
default: '',
},
})
const formRef = ref()
const state = reactive({
showDialog: false,
sureLoading: false,
form: {} as UserChangePasswordInput,
})
const { form } = toRefs(state)
//
const testNewPassword = (rule: any, value: any, callback: any) => {
if (value) {
if (state.form.confirmPassword) {
formRef.value.validateField('confirmPassword')
}
callback()
}
}
//
const testConfirmPassword = (rule: any, value: any, callback: any) => {
if (value) {
if (value !== state.form.newPassword) {
callback(new Error('确认密码和新密码不一致'))
} else {
callback()
}
}
}
//
const onInputNewPassword = (val: string) => {
state.form.newPassword = verifyCnAndSpace(val)
}
//
const onInputConfirmPassword = (val: string) => {
state.form.confirmPassword = verifyCnAndSpace(val)
}
//
const open = async () => {
state.showDialog = true
state.form = {} as UserChangePasswordInput
}
//
const onCancel = () => {
state.showDialog = false
}
//
const onSure = () => {
formRef.value.validate(async (valid: boolean) => {
if (!valid) return
state.sureLoading = true
const res = await new UserApi().changePassword(state.form, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
state.sureLoading = false
if (res?.success) {
state.showDialog = false
}
})
}
defineExpose({
open,
})
</script>

View File

@ -1,411 +0,0 @@
<template>
<div class="personal layout-pd" v-loading="state.loading">
<el-row>
<!-- 个人信息 -->
<el-col :xs="24" :sm="24">
<el-card shadow="hover" header="个人信息">
<div class="personal-user">
<div class="personal-user-left">
<el-upload
class="h100 personal-user-left-upload"
:action="avatarAction"
:headers="avatarHeaders"
:data="{ autoUpdate: true }"
:show-file-list="false"
:before-upload="
() => {
state.token = storesUserInfo.getToken()
state.avatarLoading = true
}
"
:on-success="onAvatarSuccess"
:on-error="onAvatarError"
>
<img :src="avatar" />
</el-upload>
</div>
<div class="personal-user-right">
<el-row>
<el-col :span="24" class="personal-title mb18">{{ currentTime }}{{ personalInfo.name }} </el-col>
<el-col :span="24">
<el-row>
<el-col v-if="personalForm.nickName" :xs="24" :sm="8" class="personal-item mb6">
<div class="personal-item-label">昵称</div>
<div class="personal-item-value">{{ personalInfo.nickName }}</div>
</el-col>
<el-col :xs="24" :sm="16" class="personal-item mb6">
<div class="personal-item-label">登录地区</div>
<div class="personal-item-value">
{{ personalInfo.lastLoginCountry }} {{ personalInfo.lastLoginProvince }} {{ personalInfo.lastLoginCity }}
</div>
</el-col>
</el-row>
</el-col>
<el-col :span="24">
<el-row>
<el-col :xs="24" :sm="8" class="personal-item mb6">
<div class="personal-item-label">登录IP</div>
<div class="personal-item-value">{{ personalInfo.lastLoginIP }}</div>
</el-col>
<el-col :xs="24" :sm="16" class="personal-item mb6">
<div class="personal-item-label">登录时间</div>
<div class="personal-item-value">{{ personalInfo.lastLoginTime }}</div>
</el-col>
</el-row>
</el-col>
</el-row>
</div>
</div>
</el-card>
</el-col>
<!-- 更新信息 -->
<el-col :span="24">
<el-card shadow="hover" class="mt15 personal-edit" header="更新信息">
<div class="personal-edit-title">基本信息</div>
<el-form ref="formRef" :model="personalForm" label-width="60px" class="mt35 mb35">
<el-row :gutter="35">
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4">
<el-form-item label="姓名" prop="name" :rules="[{ required: true, message: '请输入姓名', trigger: ['blur', 'change'] }]">
<el-input v-model="personalForm.name" placeholder="请输入姓名" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4">
<el-form-item label="昵称">
<el-input v-model="personalForm.nickName" placeholder="请输入昵称" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item>
<el-popconfirm title="确定要更新个人信息吗?" hide-icon hide-after="0" width="180" @confirm="onUpdateBasic">
<template #reference>
<el-button :loading="state.updateLoading" type="primary">
<el-icon>
<ele-Position />
</el-icon>
更新个人信息
</el-button>
</template>
</el-popconfirm>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div class="personal-edit-safe-box">
<div class="personal-edit-safe-item">
<div class="personal-edit-safe-item-left">
<div class="personal-edit-safe-item-left-label">密保手机</div>
<div class="personal-edit-safe-item-left-value">已绑定手机{{ personalInfo.mobile }}</div>
</div>
</div>
</div>
<div class="personal-edit-safe-box">
<div class="personal-edit-safe-item">
<div class="personal-edit-safe-item-left">
<div class="personal-edit-safe-item-left-label">密保邮箱</div>
<div class="personal-edit-safe-item-left-value">已绑定邮箱{{ personalInfo.email }}</div>
</div>
</div>
</div>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script setup lang="ts" name="admin/personal">
import { reactive, computed, onMounted, toRefs, ref, getCurrentInstance, defineAsyncComponent } from 'vue'
import { formatAxis } from '/@/utils/formatTime'
import { UserApi } from '/@/api/admin/User'
import { UserGetBasicOutput } from '/@/api/admin/data-contracts'
import { useUserInfo } from '/@/stores/userInfo'
import pinia from '/@/stores/index'
import { storeToRefs } from 'pinia'
import { AxiosResponse } from 'axios'
//
const ChangePasswordForm = defineAsyncComponent(() => import('./components/change-password-form.vue'))
const { proxy } = getCurrentInstance() as any
const changePasswordFormRef = ref()
const formRef = ref()
const storesUserInfo = useUserInfo(pinia)
const { userInfos } = storeToRefs(storesUserInfo)
//
const state = reactive({
loading: false,
newsInfoList: [] as any,
recommendList: [] as any,
personalInfo: {
avatar: '',
mobile: '',
email: '',
name: '',
nickName: '',
} as UserGetBasicOutput,
personalForm: {
name: '',
nickName: '',
},
avatarLoading: false,
updateLoading: false,
token: storesUserInfo.getToken(),
})
const { personalInfo, personalForm } = toRefs(state)
//
const currentTime = computed(() => {
return formatAxis(new Date())
})
//
const avatarHeaders = computed(() => {
return { Authorization: 'Bearer ' + state.token }
})
//
const avatar = computed(() => {
return userInfos.value.photo || 'https://img2.baidu.com/it/u=1978192862,2048448374&fm=253&fmt=auto&app=138&f=JPEG?w=504&h=500'
})
// url
const avatarAction = computed(() => {
return import.meta.env.VITE_API_URL + '/api/admin/user/avatar-upload'
})
onMounted(() => {
initData()
})
//
const initData = async () => {
state.loading = true
const res = await new UserApi().getBasic().catch(() => {
state.loading = false
})
if (res?.success) {
state.personalForm.name = res.data?.name as string
state.personalForm.nickName = res.data?.nickName as string
state.personalInfo = res.data as UserGetBasicOutput
}
state.loading = false
}
//
const onAvatarSuccess = (res: AxiosResponse) => {
state.avatarLoading = false
if (!res?.success) {
if (res.msg) {
proxy.$modal.msgError(res.msg)
}
return
}
state.personalInfo.avatar = res.data
storesUserInfo.setPhoto(res.data)
}
//
const onAvatarError = (error: any) => {
state.avatarLoading = false
let message = ''
if (error.message) {
try {
message = JSON.parse(error.message)?.msg
} catch (err) {
message = error.message || ''
}
}
if (message) proxy.$modal.msgError(message)
}
//
const onUpdateBasic = async () => {
formRef.value.validate(async (valid: boolean) => {
if (!valid) return
state.updateLoading = true
const res = await new UserApi().updateBasic(state.personalForm, { showSuccessMessage: true }).catch(() => {
state.updateLoading = false
})
state.updateLoading = false
if (res?.success) {
state.personalInfo.nickName = state.personalForm.nickName
state.personalInfo.name = state.personalForm.name
storesUserInfo.setUserName(state.personalForm.nickName || state.personalForm.name)
}
})
}
//
const onChangePassword = () => {
changePasswordFormRef.value.open()
}
</script>
<style scoped lang="scss">
@use '/@/theme/mixins/index.scss' as *;
.personal {
.personal-user {
height: 130px;
display: flex;
align-items: center;
.personal-user-left {
width: 100px;
height: 130px;
border-radius: 3px;
:deep(.el-upload) {
height: 100%;
}
.personal-user-left-upload {
img {
width: 100%;
height: 100%;
border-radius: 3px;
}
&:hover {
img {
animation: logoAnimation 0.3s ease-in-out;
}
}
}
}
.personal-user-right {
flex: 1;
padding: 0 15px;
.personal-title {
font-size: 18px;
@include text-ellipsis(1);
}
.personal-item {
display: flex;
align-items: center;
font-size: 13px;
.personal-item-label {
color: var(--el-text-color-secondary);
@include text-ellipsis(1);
}
.personal-item-value {
@include text-ellipsis(1);
}
}
}
}
.personal-info {
.personal-info-more {
float: right;
color: var(--el-text-color-secondary);
font-size: 13px;
&:hover {
color: var(--el-color-primary);
cursor: pointer;
}
}
.personal-info-box {
height: 130px;
overflow: hidden;
.personal-info-ul {
list-style: none;
.personal-info-li {
font-size: 13px;
padding-bottom: 10px;
.personal-info-li-title {
display: inline-block;
@include text-ellipsis(1);
color: var(--el-text-color-secondary);
text-decoration: none;
}
& a:hover {
color: var(--el-color-primary);
cursor: pointer;
}
}
}
}
}
.personal-recommend-row {
.personal-recommend-col {
.personal-recommend {
position: relative;
height: 100px;
border-radius: 3px;
overflow: hidden;
cursor: pointer;
&:hover {
i {
right: 0px !important;
bottom: 0px !important;
transition: all ease 0.3s;
}
}
i {
position: absolute;
right: -10px;
bottom: -10px;
font-size: 70px;
transform: rotate(-30deg);
transition: all ease 0.3s;
}
.personal-recommend-auto {
padding: 15px;
position: absolute;
left: 0;
top: 5%;
color: var(--next-color-white);
.personal-recommend-msg {
font-size: 12px;
margin-top: 10px;
}
}
}
}
}
.personal-edit {
.personal-edit-title {
position: relative;
padding-left: 10px;
color: var(--el-text-color-regular);
&::after {
content: '';
width: 2px;
height: 10px;
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
background: var(--el-color-primary);
}
}
.personal-edit-safe-box {
border-bottom: 1px solid var(--el-border-color-light, #ebeef5);
padding: 15px 0;
.personal-edit-safe-item {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
.personal-edit-safe-item-left {
flex: 1;
overflow: hidden;
.personal-edit-safe-item-left-label {
color: var(--el-text-color-regular);
margin-bottom: 5px;
}
.personal-edit-safe-item-left-value {
color: var(--el-text-color-secondary);
@include text-ellipsis(1);
margin-right: 15px;
}
}
}
&:last-of-type {
padding-bottom: 0;
border-bottom: none;
}
}
}
}
</style>

View File

@ -1,125 +0,0 @@
<template>
<div>
<el-dialog
v-model="state.showDialog"
destroy-on-close
:title="title"
draggable
:close-on-click-modal="false"
:close-on-press-escape="false"
width="600px"
>
<el-form :model="form" ref="formRef" label-width="80px">
<el-row :gutter="35">
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="名称" prop="name" :rules="[{ required: true, message: '请输入名称', trigger: ['blur', 'change'] }]">
<el-input v-model="form.name" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="编码" prop="code">
<el-input v-model="form.code" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="排序">
<el-input-number v-model="form.sort" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="启用">
<el-switch v-model="form.enabled" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="说明">
<el-input v-model="form.description" clearable type="textarea" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel"> </el-button>
<el-button type="primary" @click="onSure" :loading="state.sureLoading"> </el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup name="admin/pkg/form">
import { reactive, toRefs, ref } from 'vue'
import { PkgUpdateInput } from '/@/api/admin/data-contracts'
import { PkgApi } from '/@/api/admin/Pkg'
import { cloneDeep } from 'lodash-es'
import eventBus from '/@/utils/mitt'
defineProps({
title: {
type: String,
default: '',
},
})
const formRef = ref()
const state = reactive({
showDialog: false,
sureLoading: false,
form: {} as PkgUpdateInput,
})
const { form } = toRefs(state)
//
const open = async (row: PkgUpdateInput = { id: 0 }) => {
let formData = cloneDeep(row) as PkgUpdateInput
if (row.id > 0) {
const res = await new PkgApi().get({ id: row.id }, { loading: true })
if (res?.success) {
formData = res.data as PkgUpdateInput
formData.parentId = formData.parentId && formData.parentId > 0 ? formData.parentId : undefined
}
}
state.form = formData
state.showDialog = true
}
//
const onCancel = () => {
state.showDialog = false
}
//
const onSure = () => {
formRef.value.validate(async (valid: boolean) => {
if (!valid) return
state.sureLoading = true
let res = {} as any
state.form.parentId = state.form.parentId && state.form.parentId > 0 ? state.form.parentId : undefined
if (state.form.id != undefined && state.form.id > 0) {
res = await new PkgApi().update(state.form, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
} else {
res = await new PkgApi().add(state.form, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
}
state.sureLoading = false
if (res?.success) {
eventBus.emit('refreshPkg')
state.showDialog = false
}
})
}
defineExpose({
open,
})
</script>

View File

@ -1,217 +0,0 @@
<template>
<el-dialog
v-model="state.showDialog"
destroy-on-close
:title="innerTitle"
append-to-body
draggable
:close-on-click-modal="false"
:close-on-press-escape="false"
width="780px"
>
<template #header="{ close, titleId, titleClass }">
<div class="my-header">
<div :id="titleId" :class="titleClass">
设置{{ innerTitle }}
<el-select v-model="state.platform" placeholder="请选择所属平台" style="width: 100px" @change="onQuery">
<el-option v-for="item in state.dictData[DictType.PlatForm.name]" :key="item.code" :label="item.name" :value="item.code" />
</el-select>
菜单权限
</div>
</div>
</template>
<div>
<el-tree
ref="permissionTreeRef"
:data="state.permissionTreeData"
node-key="id"
show-checkbox
highlight-current
default-expand-all
check-on-click-node
:expand-on-click-node="false"
:props="{ class: customNodeClass }"
:default-checked-keys="state.checkedKeys"
/>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel"> </el-button>
<el-button type="primary" @click="onSure" :loading="state.sureLoading"> </el-button>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup name="admin/pkg/components/set-pkg-menu">
import { ref, reactive, getCurrentInstance, computed, markRaw } from 'vue'
import { PkgGetListOutput, PkgSetPkgPermissionsInput, DictGetListOutput, PermissionGetPermissionListOutput } from '/@/api/admin/data-contracts'
import { PkgApi } from '/@/api/admin/Pkg'
import { PermissionApi } from '/@/api/admin/Permission'
import { ElTree } from 'element-plus'
import { DictApi } from '/@/api/admin/Dict'
import { PlatformType } from '/@/api/admin.extend/enum-contracts'
/** 字典分类 */
const DictType = {
PlatForm: { name: 'platform', desc: '平台' },
}
const props = defineProps({
title: {
type: String,
default: '',
},
})
const innerTitle = computed(() => {
return props.title ? props.title : state.pkgName ? `设置【${state.pkgName}】菜单权限` : '设置菜单权限'
})
const state = reactive({
showDialog: false,
loading: false,
sureLoading: false,
permissionTreeData: [] as PermissionGetPermissionListOutput[],
pkgId: 0 as number | undefined,
pkgName: '' as string | undefined | null,
checkedKeys: [],
platform: PlatformType.Web.name,
dictData: {
[DictType.PlatForm.name]: [] as DictGetListOutput[] | null,
},
})
const { proxy } = getCurrentInstance() as any
const permissionTreeRef = ref<InstanceType<typeof ElTree>>()
const getDictList = async () => {
const res = await new DictApi().getList([DictType.PlatForm.name]).catch(() => {})
if (res?.success && res.data) {
state.dictData = markRaw(res.data)
}
}
const getPkgPermissionList = async () => {
const res = await new PkgApi().getPkgPermissionList({ pkgId: state.pkgId })
state.checkedKeys = res?.success ? (res.data as never[]) : []
}
//
const open = async (pkg: PkgGetListOutput) => {
await getDictList()
state.pkgId = pkg.id
state.pkgName = pkg.name
proxy.$modal.loading()
await onQuery()
await getPkgPermissionList()
proxy.$modal.closeLoading()
state.showDialog = true
}
//
const close = () => {
state.showDialog = false
}
const onQuery = async () => {
state.loading = true
const res = await new PermissionApi().getPermissionList({ platform: state.platform }).catch(() => {
state.loading = false
})
if (res && res.data && res.data.length > 0) {
state.permissionTreeData = res.data
} else {
state.permissionTreeData = []
}
state.loading = false
}
const customNodeClass = (data: any, node: any) => {
let isPenultimate = true
for (const key in data.children) {
if (data.children[key]?.children?.length ?? 0 > 0) {
isPenultimate = false
break
}
}
return data.children?.length > 0 && isPenultimate ? `is-penultimate level${node.level}` : ''
}
//
const onCancel = () => {
state.showDialog = false
}
//
const onSure = async () => {
state.sureLoading = true
const permissionIds = permissionTreeRef.value?.getCheckedKeys(true)
const input = {
platform: state.platform,
pkgId: state.pkgId,
permissionIds: permissionIds,
} as PkgSetPkgPermissionsInput
const res = await new PkgApi().setPkgPermissions(input, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
state.sureLoading = false
if (res?.success) {
state.showDialog = false
}
}
defineExpose({
open,
close,
})
</script>
<style scoped lang="scss">
:deep(.el-dialog__body) {
padding: 5px 10px;
}
:deep(.is-penultimate) {
.el-tree-node__children {
padding-left: 65px;
white-space: pre-wrap;
line-height: 100%;
.el-tree-node {
display: inline-block;
}
.el-tree-node__content {
padding-left: 12px !important;
padding-right: 12px;
.el-tree-node__expand-icon.is-leaf {
display: none;
}
}
}
&.level1 {
.el-tree-node__children {
padding-left: 36px;
}
}
&.level2 {
.el-tree-node__children {
padding-left: 54x;
}
}
&.level3 {
.el-tree-node__children {
padding-left: 72px;
}
}
&.level4 {
.el-tree-node__children {
padding-left: 90px;
}
}
}
</style>

View File

@ -1,338 +0,0 @@
<template>
<MySplitPanes>
<pane size="50" min-size="30" max-size="70">
<div class="my-flex-column w100 h100">
<el-card class="my-query-box mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
<el-form :inline="true" @submit.stop.prevent>
<el-form-item label="套餐名">
<el-input v-model="state.filter.pkgName" placeholder="套餐名" @keyup.enter="onQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="ele-Search" @click="onQuery"> 查询 </el-button>
<el-button v-auth="'api:admin:pkg:add'" type="primary" icon="ele-Plus" @click="onAdd"> 新增 </el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="my-fill mt8" shadow="never">
<el-table
ref="pkgTableRef"
v-loading="state.loading"
:data="state.pkgData"
default-expand-all
highlight-current-row
style="width: 100%"
border
@current-change="onTableCurrentChange"
>
<el-table-column prop="name" label="套餐名" min-width="120" show-overflow-tooltip />
<el-table-column prop="sort" label="排序" width="82" align="center" show-overflow-tooltip />
<el-table-column label="操作" width="100" fixed="right" header-align="center" align="center">
<template #default="{ row }">
<my-dropdown-more
v-auths="['api:admin:pkg:set-pkg-permissions', 'api:admin:pkg:update', 'api:admin:pkg:delete']"
style="margin-left: 0px"
>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item v-if="auth('api:admin:pkg:set-pkg-permissions')" @click="onSetPkgMenu(row)">菜单权限</el-dropdown-item>
<el-dropdown-item v-if="auth('api:admin:pkg:update')" @click="onEdit(row)">编辑套餐</el-dropdown-item>
<el-dropdown-item v-if="auth('api:admin:pkg:delete')" @click="onDelete(row)">删除套餐</el-dropdown-item>
</el-dropdown-menu>
</template>
</my-dropdown-more>
</template>
</el-table-column>
</el-table>
<div class="my-flex my-flex-end" style="margin-top: 10px">
<el-pagination
v-model:currentPage="state.pageInput.currentPage"
v-model:page-size="state.pageInput.pageSize"
:total="state.total"
:page-sizes="[10, 20, 50, 100]"
background
@size-change="onSizeChange"
@current-change="onCurrentChange"
layout="total, sizes, prev, pager, next, jumper"
/>
</div>
</el-card>
</div>
</pane>
<pane>
<div class="my-flex-column w100 h100">
<el-card class="my-query-box mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
<el-form :inline="true" @submit.stop.prevent>
<el-form-item label="企业名">
<el-input v-model="state.filter.name" placeholder="企业名" @keyup.enter="onGetPkgTenantList" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="ele-Search" @click="onGetPkgTenantList"> 查询 </el-button>
<el-button v-auth="'api:admin:pkg:add-pkg-tenant'" type="primary" icon="ele-Plus" @click="onAddTenant"> 添加企业 </el-button>
<el-button v-auth="'api:admin:pkg:remove-pkg-tenant'" type="danger" icon="ele-Delete" @click="onRemoveTenant"> 移除企业 </el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="my-fill mt8" shadow="never">
<el-table
ref="tenantTableRef"
v-loading="state.tenantListLoading"
:data="state.tenantData"
row-key="id"
style="width: 100%"
border
@row-click="onTenantRowClick"
>
<el-table-column type="selection" width="55" />
<el-table-column prop="name" label="企业名" min-width="120" show-overflow-tooltip />
<el-table-column prop="code" label="企业编码" min-width="120" show-overflow-tooltip />
</el-table>
<div class="my-flex my-flex-end" style="margin-top: 10px">
<el-pagination
v-model:currentPage="state.tenantPageInput.currentPage"
v-model:page-size="state.tenantPageInput.pageSize"
:total="state.tenantTotal"
:page-sizes="[10, 20, 50, 100]"
background
@size-change="onTenantSizeChange"
@current-change="onTenantCurrentChange"
layout="total, sizes, prev, pager, next, jumper"
/>
</div>
</el-card>
</div>
</pane>
<pkg-form ref="pkgFormRef" :title="state.pkgFormTitle"></pkg-form>
<tenant-select
ref="tenantSelectRef"
:title="`添加【${state.pkgName}】企业`"
multiple
:sure-loading="state.sureLoading"
@sure="onSureTenant"
></tenant-select>
<set-pkg-menu ref="setPkgMenuRef"></set-pkg-menu>
</MySplitPanes>
</template>
<script lang="ts" setup name="admin/pkg">
import { ref, reactive, onMounted, getCurrentInstance, onBeforeMount, nextTick, defineAsyncComponent } from 'vue'
import {
PageInputPkgGetPageInput,
PkgGetPkgTenantListOutput,
PkgGetPageOutput,
PkgAddPkgTenantListInput,
PageInputPkgGetPkgTenantListInput,
} from '/@/api/admin/data-contracts'
import { PkgApi } from '/@/api/admin/Pkg'
import { ElTable } from 'element-plus'
import eventBus from '/@/utils/mitt'
import { auth } from '/@/utils/authFunction'
import { Pane } from 'splitpanes'
//
const PkgForm = defineAsyncComponent(() => import('./components/pkg-form.vue'))
const SetPkgMenu = defineAsyncComponent(() => import('./components/set-pkg-menu.vue'))
const TenantSelect = defineAsyncComponent(() => import('/@/views/admin/tenant/components/tenant-select.vue'))
const MyDropdownMore = defineAsyncComponent(() => import('/@/components/my-dropdown-more/index.vue'))
const MySplitPanes = defineAsyncComponent(() => import('/@/components/my-layout/split-panes.vue'))
const { proxy } = getCurrentInstance() as any
const pkgTableRef = ref()
const pkgFormRef = ref()
const tenantTableRef = ref<InstanceType<typeof ElTable>>()
const tenantSelectRef = ref()
const setPkgMenuRef = ref()
const state = reactive({
loading: false,
tenantListLoading: false,
sureLoading: false,
pkgFormTitle: '',
filter: {
name: '',
pkgName: '',
},
total: 0,
pageInput: {
currentPage: 1,
pageSize: 20,
filter: {
name: '',
},
} as PageInputPkgGetPageInput,
pkgData: [] as any,
tenantPageInput: {
currentPage: 1,
pageSize: 20,
filter: {
pkgId: null,
tenantName: '',
},
} as PageInputPkgGetPkgTenantListInput,
tenantData: [] as PkgGetPkgTenantListOutput[],
tenantTotal: 0,
pkgId: undefined as number | undefined,
pkgName: '' as string | null | undefined,
})
onMounted(() => {
onQuery()
eventBus.off('refreshPkg')
eventBus.on('refreshPkg', async () => {
onQuery()
})
})
onBeforeMount(() => {
eventBus.off('refreshPkg')
})
const onQuery = async () => {
state.loading = true
if (state.pageInput.filter) state.pageInput.filter.name = state.filter.pkgName
const res = await new PkgApi().getPage(state.pageInput).catch(() => {
state.loading = false
})
state.pkgData = res?.data?.list ?? []
state.total = res?.data?.total ?? 0
if (state.pkgData?.length > 0) {
nextTick(() => {
pkgTableRef.value!.setCurrentRow(state.pkgData[0])
})
}
state.loading = false
}
const onSizeChange = (val: number) => {
state.pageInput.currentPage = 1
state.pageInput.pageSize = val
onQuery()
}
const onCurrentChange = (val: number) => {
state.pageInput.currentPage = val
onQuery()
}
const onTableCurrentChange = (currentRow: PkgGetPageOutput) => {
if (!currentRow) {
return
}
state.pkgId = currentRow.id
state.pkgName = currentRow.name
onGetPkgTenantList()
}
const onAdd = () => {
state.pkgFormTitle = '新增套餐'
pkgFormRef.value.open({ enabled: true })
}
const onEdit = (row: PkgGetPageOutput) => {
state.pkgFormTitle = '编辑套餐'
pkgFormRef.value.open(row)
}
const onDelete = (row: PkgGetPageOutput) => {
proxy.$modal
.confirmDelete(`确定要删除套餐【${row.name}】?`)
.then(async () => {
await new PkgApi().delete({ id: row.id }, { loading: true })
onQuery()
})
.catch(() => {})
}
const onGetPkgTenantList = async () => {
state.tenantListLoading = true
state.tenantPageInput.filter = { pkgId: state.pkgId, tenantName: state.filter.name }
const res = await new PkgApi().getPkgTenantPage(state.tenantPageInput).catch(() => {
state.tenantListLoading = false
})
state.tenantListLoading = false
if (res?.success) {
state.tenantData = res?.data?.list ?? []
state.tenantTotal = res?.data?.total ?? 0
}
}
const onTenantSizeChange = (val: number) => {
state.tenantPageInput.pageSize = val
onGetPkgTenantList()
}
const onTenantCurrentChange = (val: number) => {
state.tenantPageInput.currentPage = val
onGetPkgTenantList()
}
const onTenantRowClick = (row: PkgGetPkgTenantListOutput) => {
tenantTableRef.value!.toggleRowSelection(row, undefined)
}
const onAddTenant = () => {
if (!((state.pkgId as number) > 0)) {
proxy.$modal.msgWarning('请选择套餐')
return
}
tenantSelectRef.value.open({ pkgId: state.pkgId })
}
const onRemoveTenant = () => {
if (!((state.pkgId as number) > 0)) {
proxy.$modal.msgWarning('请选择套餐')
return
}
const selectionRows = tenantTableRef.value!.getSelectionRows() as PkgGetPageOutput[]
if (!((selectionRows.length as number) > 0)) {
proxy.$modal.msgWarning('请选择租户')
return
}
proxy.$modal
.confirm(`确定要移除吗?`)
.then(async () => {
const tenantIds = selectionRows?.map((a) => a.id)
const input = { pkgId: state.pkgId, tenantIds } as PkgAddPkgTenantListInput
await new PkgApi().removePkgTenant(input, { loading: true })
onGetPkgTenantList()
})
.catch(() => {})
}
const onSureTenant = async (tenants: PkgGetPageOutput[]) => {
if (!(tenants?.length > 0)) {
tenantSelectRef.value.close()
return
}
state.sureLoading = true
const tenantIds = tenants?.map((a) => a.id)
const input = { pkgId: state.pkgId, tenantIds } as PkgAddPkgTenantListInput
await new PkgApi().addPkgTenant(input, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
state.sureLoading = false
tenantSelectRef.value.close()
onGetPkgTenantList()
}
const onSetPkgMenu = (pkg: PkgGetPageOutput) => {
if (!((pkg?.id as number) > 0)) {
proxy.$modal.msgWarning('请选择套餐')
return
}
setPkgMenuRef.value.open(pkg)
}
</script>
<style scoped lang="scss"></style>

View File

@ -1,236 +0,0 @@
<template>
<el-drawer v-model="state.visible" direction="ltr" destroy-on-close size="100%" @closed="onClosed">
<template #header="{ titleId, titleClass }">
<div class="my-flex my-flex-between mr10">
<span :id="titleId" :class="titleClass">{{ title }}</span>
<div>
<el-button ref="saveRef" type="primary" round :loading="state.saveLoading">
<template #icon>
<el-icon>
<my-icon name="save" color="var(--color)"></my-icon>
</el-icon>
</template>
保存
</el-button>
<el-popover ref="popoverSaveRef" placement="bottom-end" :virtual-ref="saveRef" trigger="click" virtual-triggering width="auto" title="提示">
<p class="my-flex my-flex-items-center">
<SvgIcon name="ele-Warning" size="16" color="var(--el-color-warning)" class="mr5" />
确定要保存设计模板吗
</p>
<div class="mt10" style="text-align: right; margin: 0">
<el-button text @click="onSaveCancel">取消</el-button>
<el-button type="primary" @click="onSave"> 保存并关闭 </el-button>
<el-button type="primary" @click="onSave(false)"> 保存 </el-button>
</div>
</el-popover>
<el-tooltip content="刷新" placement="bottom">
<el-button ref="refreshRef" link>
<template #icon>
<el-icon size="18px">
<ele-Refresh></ele-Refresh>
</el-icon>
</template>
</el-button>
</el-tooltip>
<el-popover ref="popoverRefreshRef" placement="bottom" :virtual-ref="refreshRef" trigger="click" virtual-triggering :width="230">
<p class="my-flex my-flex-items-center">确定要刷新设计模板吗</p>
<div class="mt10" style="text-align: right; margin: 0">
<el-button text @click="onRefreshCancel">取消</el-button>
<el-button type="primary" @click="onRefresh">确定</el-button>
</div>
</el-popover>
</div>
</div>
</template>
<Design ref="designRef" :title="title" v-model:printData="state.printTemplate.printData"></Design>
</el-drawer>
</template>
<script lang="ts" setup name="admin/print-template-deisgn">
import { reactive, ref, getCurrentInstance, nextTick } from 'vue'
import Design from './design.vue'
import { PrintTemplateGetPageOutput } from '/@/api/admin/data-contracts'
import eventBus from '/@/utils/mitt'
import { PrintTemplateApi } from '/@/api/admin/PrintTemplate'
defineProps({
title: {
type: String,
default: '',
},
})
const { proxy } = getCurrentInstance() as any
const designRef = ref()
const saveRef = ref()
const popoverSaveRef = ref()
const refreshRef = ref()
const popoverRefreshRef = ref()
const state = reactive({
visible: false,
saveLoading: false,
refreshLoading: false,
printTemplate: {
id: 0,
version: 0,
printData: '{}',
},
})
const loadData = async () => {
if (state.printTemplate.id > 0) {
state.refreshLoading = true
const res = await new PrintTemplateApi().getUpdateTemplate({ id: state.printTemplate.id }).catch(() => {
state.refreshLoading = false
})
state.refreshLoading = false
if (res?.success) {
const printTemplate = res.data
state.printTemplate.id = printTemplate?.id as number
state.printTemplate.version = printTemplate?.version as number
state.printTemplate.printData = printTemplate?.printData || ('{}' as string)
nextTick(() => {
designRef.value?.hiprintTemplate.clear()
const template = JSON.parse(printTemplate?.template || '{}')
designRef.value?.hiprintTemplate.update(template)
designRef.value?.setPaper(template)
})
}
}
}
const onSaveCancel = () => {
popoverSaveRef.value?.hide?.()
}
const onRefreshCancel = () => {
popoverRefreshRef.value?.hide?.()
}
const onRefresh = () => {
onRefreshCancel()
loadData()
}
//
const onSave = async (close = true) => {
try {
if (designRef.value?.hiprintTemplate) {
if (state.printTemplate.id != undefined && state.printTemplate.id > 0) {
state.saveLoading = true
const res = await new PrintTemplateApi()
.updateTemplate(
{
id: state.printTemplate.id,
version: state.printTemplate.version,
template: JSON.stringify(designRef.value?.hiprintTemplate.getJson() || {}),
printData: state.printTemplate.printData as string,
},
{ showSuccessMessage: true }
)
.catch(() => {
state.saveLoading = false
})
state.printTemplate.version = state.printTemplate.version + 1
state.saveLoading = false
onSaveCancel()
if (res?.success) {
eventBus.emit('refreshPrintTemplate')
if (close) state.visible = false
}
} else {
proxy.$modal.msgWarning('请选择打印模板')
}
}
} catch (error) {}
}
//
const open = async (row: PrintTemplateGetPageOutput = {}) => {
state.visible = true
state.printTemplate.id = row.id as number
await loadData()
}
//
const onClosed = () => {
state.visible = false
}
defineExpose({
open,
})
</script>
<style scoped lang="scss">
:deep() {
.left-box {
.el-tabs__header {
margin-bottom: 0px;
}
.el-tab-pane {
height: 100%;
}
}
.rect-printElement-types .hiprint-printElement-type a.ep-draggable-item {
height: auto;
color: #666;
box-shadow: none !important;
text-overflow: ellipsis;
}
.hiprint-printElement-type a.ep-draggable-item:before {
display: block;
width: 30px;
height: 30px;
content: '';
background-repeat: no-repeat;
background-size: contain;
margin: 0 auto;
}
.hiprint-printElement-type a.ep-draggable-item[tid='comModule.hline']:before {
background-image: url('/@/assets/svgs/hiprint/hline.svg');
}
.hiprint-printElement-type a.ep-draggable-item[tid='comModule.vline']:before {
background-image: url('/@/assets/svgs/hiprint/vline.svg');
}
.hiprint-printElement-type a.ep-draggable-item[tid='comModule.rect']:before {
background-image: url('/@/assets/svgs/hiprint/rect.svg');
}
.hiprint-printElement-type a.ep-draggable-item[tid='comModule.oval']:before {
background-image: url('/@/assets/svgs/hiprint/oval.svg');
}
.hiprint-printElement-type a.ep-draggable-item[tid='comModule.barcode']:before {
background-image: url('/@/assets/svgs/hiprint/barcode.svg');
}
.hiprint-printElement-type a.ep-draggable-item[tid='comModule.qrcode']:before {
background-image: url('/@/assets/svgs/hiprint/qrcode.svg');
}
.hiprint-printElement-type a.ep-draggable-item[tid='comModule.text']:before {
background-image: url('/@/assets/svgs/hiprint/text.svg');
}
.hiprint-printElement-type a.ep-draggable-item[tid='comModule.longText']:before {
background-image: url('/@/assets/svgs/hiprint/longText.svg');
}
.hiprint-printElement-type a.ep-draggable-item[tid='comModule.table']:before {
background-image: url('/@/assets/svgs/hiprint/table.svg');
}
.hiprint-printElement-type a.ep-draggable-item[tid='comModule.emptyTable']:before {
background-image: url('/@/assets/svgs/hiprint/emptyTable.svg');
}
.hiprint-printElement-type a.ep-draggable-item[tid='comModule.html']:before {
background-image: url('/@/assets/svgs/hiprint/html.svg');
}
.hiprint-printElement-type a.ep-draggable-item[tid='comModule.image']:before {
background-image: url('/@/assets/svgs/hiprint/image.svg');
}
}
</style>

View File

@ -1,594 +0,0 @@
<template>
<div class="h100 w100 my-design" style="position: relative">
<div class="my-flex w100 h100">
<div style="width: 220px; min-width: 220px; border-right: 1px solid var(--el-border-color)">
<el-tabs tab-position="top" stretch class="h100 left-box">
<el-tab-pane label="组件">
<el-scrollbar height="100%" max-height="100%" :always="false" wrap-style="padding:10px">
<!-- 拖拽组件 -->
<div ref="epContainerRef" class="rect-printElement-types hiprintEpContainer"></div>
</el-scrollbar>
</el-tab-pane>
<el-tab-pane label="数据源">
<MyJsonEditor
v-model="printData"
:options="{
mode: 'text',
mainMenuBar: true,
statusBar: false,
showErrorTable: false,
modes: [],
}"
></MyJsonEditor>
</el-tab-pane>
</el-tabs>
</div>
<div class="my-fill" style="overflow: hidden; min-width: 520px">
<!-- 操作栏 -->
<div style="padding: 10px 10px 0px 10px; border-bottom: 1px solid var(--el-border-color)">
<div class="my-flex my-flex-wrap">
<div>
<!-- 纸张 -->
<el-select v-model="state.curPaper.type" placeholder="纸张" class="mr2 mb10" style="width: 70px" @change="onSetPaper">
<el-option v-for="item in state.paperTypes" :key="item.type" :label="item.type" :value="item.type" />
</el-select>
<!-- 自定义纸张 -->
<el-tooltip content="自定义纸张" placement="top">
<el-button ref="paperRef" :type="state.curPaper.type === '' ? 'primary' : ''" class="mr10 mb10">
<el-icon>
<my-icon name="customSize" color="var(--color)"></my-icon>
</el-icon>
</el-button>
</el-tooltip>
<el-popover ref="popoverRef" :virtual-ref="paperRef" trigger="click" virtual-triggering width="auto" title="设置纸张宽高(mm)">
<p class="my-flex my-flex-items-center my-flex-between">
<el-input-number
v-model="state.customPaper.width"
placeholder="宽"
:precision="1"
:step="1"
min="0"
controls-position="right"
style="width: 110px"
class="mr6"
>
</el-input-number>
~
<el-input-number
v-model="state.customPaper.height"
placeholder="高"
:precision="1"
:step="1"
min="0"
controls-position="right"
style="width: 110px"
class="ml6"
>
</el-input-number>
</p>
<div class="mt10">
<el-button type="primary" class="w100" @click="onCustomPaper"> 确定 </el-button>
</div>
</el-popover>
<!-- 缩放 -->
<el-input-number
v-model="state.scaleValue"
:precision="1"
:step="0.1"
min="0.5"
max="5"
class="mr10 mb10"
@change="onChangeScale"
style="width: 120px"
>
<template #decrease-icon>
<SvgIcon name="ele-ZoomOut" />
</template>
<template #increase-icon>
<SvgIcon name="ele-ZoomIn" />
</template>
</el-input-number>
</div>
<!-- 排版 -->
<el-button-group class="my-flex mr10 mb10">
<el-tooltip content="左对齐" placement="top">
<el-button @click="onSetElsAlign('left')">
<el-icon>
<my-icon name="left" color="var(--color)"></my-icon>
</el-icon>
</el-button>
</el-tooltip>
<el-tooltip content="居中" placement="top">
<el-button @click="onSetElsAlign('vertical')">
<el-icon>
<my-icon name="vertical" color="var(--color)"></my-icon>
</el-icon>
</el-button>
</el-tooltip>
<el-tooltip content="右对齐" placement="top">
<el-button @click="onSetElsAlign('right')">
<el-icon>
<my-icon name="right" color="var(--color)"></my-icon>
</el-icon>
</el-button>
</el-tooltip>
<el-tooltip content="顶对齐" placement="top">
<el-button @click="onSetElsAlign('top')">
<el-icon>
<my-icon name="top" color="var(--color)"></my-icon>
</el-icon>
</el-button>
</el-tooltip>
<el-tooltip content="垂直居中" placement="top">
<el-button @click="onSetElsAlign('horizontal')">
<el-icon>
<my-icon name="horizontal" color="var(--color)"></my-icon>
</el-icon>
</el-button>
</el-tooltip>
<el-tooltip content="底对齐" placement="top">
<el-button @click="onSetElsAlign('bottom')">
<el-icon>
<my-icon name="bottom" color="var(--color)"></my-icon>
</el-icon>
</el-button>
</el-tooltip>
<el-tooltip content="横向分散" placement="top">
<el-button @click="onSetElsAlign('distributeHor')">
<el-icon>
<my-icon name="distributeHor" color="var(--color)"></my-icon>
</el-icon>
</el-button>
</el-tooltip>
<el-tooltip content="纵向分散" placement="top">
<el-button @click="onSetElsAlign('distributeVer')">
<el-icon>
<my-icon name="distributeVer" color="var(--color)"></my-icon>
</el-icon>
</el-button>
</el-tooltip>
<el-tooltip content="旋转" placement="top">
<el-button @click="onRotatePaper">
<el-icon>
<my-icon name="rotate" color="var(--color)"></my-icon>
</el-icon>
</el-button>
</el-tooltip>
</el-button-group>
<!-- 操作 -->
<el-button-group class="my-flex mb10">
<el-tooltip content="预览" placement="top">
<el-button icon="ele-View" @click="onPreView"></el-button>
</el-tooltip>
<el-tooltip content="清空" placement="top">
<el-button icon="ele-Delete" @click="onClearPaper"></el-button>
</el-tooltip>
<el-tooltip content="打印" placement="top">
<el-button icon="ele-Printer" @click="onPrint"> </el-button>
</el-tooltip>
<el-tooltip content="查看模板JSON" placement="top">
<el-button @click="onViewJson">
<el-icon>
<my-icon name="json" color="var(--color)"></my-icon>
</el-icon>
</el-button>
</el-tooltip>
</el-button-group>
</div>
</div>
<el-scrollbar
ref="printTemplateScrollbarRef"
height="100%"
max-height="100%"
:always="false"
wrap-style="background-color: #fff;color: #333;"
>
<!-- 画布 -->
<div ref="designRef" class="hiprint-printTemplate"></div>
</el-scrollbar>
</div>
<div style="width: 350px; min-width: 350px; border-left: 1px solid var(--el-border-color)">
<el-scrollbar height="100%" max-height="100%" :always="false" wrap-style="padding:0px">
<!-- 配置 -->
<div id="hiprint-printElementOptionSetting"></div>
</el-scrollbar>
</div>
</div>
</div>
<PrintPreview ref="previewRef"></PrintPreview>
<ViewJson ref="previewJsonDialogRef" title="查看模板JSON"></ViewJson>
</template>
<script lang="ts" setup name="admin/print-template-deisgn">
import { reactive, ref, onMounted, nextTick, getCurrentInstance } from 'vue'
import { hiprint } from 'vue-plugin-hiprint'
import providers from './providers'
import PrintPreview from './preview.vue'
import ViewJson from './view-json.vue'
import MyJsonEditor from '/@/components/my-json-editor/index.vue'
const title = defineModel('title', { type: String })
const printData = defineModel('printData', { type: String })
interface IPaperType {
type: string
width: number
height: number
}
const state = reactive({
visible: false,
sureLoading: false,
scaleValue: 1,
//
curPaper: {
type: 'A4',
width: 210,
height: 296.6,
} as IPaperType,
//
customPaper: {
type: '',
width: 220,
height: 80,
} as IPaperType,
//
paperTypes: [
{
type: 'A3',
width: 420,
height: 296.6,
},
{
type: 'A4',
width: 210,
height: 296.6,
},
{
type: 'A5',
width: 210,
height: 147.6,
},
{
type: 'B3',
width: 500,
height: 352.6,
},
{
type: 'B4',
width: 250,
height: 352.6,
},
{
type: 'B5',
width: 250,
height: 175.6,
},
] as IPaperType[],
showSaveDialog: false,
})
const { proxy } = getCurrentInstance() as any
let hiprintTemplate = ref()
const paperRef = ref()
const popoverRef = ref()
const printTemplateScrollbarRef = ref()
const previewRef = ref()
const previewJsonDialogRef = ref()
const epContainerRef = ref()
const designRef = ref()
const epDraggableItemRef = ref()
onMounted(() => {
buildProvider()
buildDesigner()
})
//
const buildProvider = () => {
let provider = providers[0]
hiprint.init({ providers: [provider.f] })
if (epContainerRef.value) {
epContainerRef.value.innerHTML = ''
}
hiprint.PrintElementTypeManager.build(epContainerRef.value, provider.value)
}
//
const buildDesigner = (template = {} as any) => {
if (designRef.value) {
designRef.value.innerHTML = ''
}
hiprintTemplate.value = new hiprint.PrintTemplate({
template: template,
settingContainer: '#hiprint-printElementOptionSetting',
paginationContainer: '.hiprint-printPagination',
fontList: [
{ title: '微软雅黑', value: 'Microsoft YaHei' },
{ title: '黑体', value: 'STHeitiSC-Light' },
{ title: '思源黑体', value: 'SourceHanSansCN-Normal' },
{ title: '王羲之书法体', value: '王羲之书法体' },
{ title: '宋体', value: 'SimSun' },
{ title: '华为楷体', value: 'STKaiti' },
{ title: 'cursive', value: 'cursive' },
],
history: true,
})
hiprintTemplate.value.design(designRef.value)
designRef.value.querySelector('.hiprint-printPaper')?.firstChild.click()
}
const setPaper = (template = {} as any) => {
if (template?.panels?.length > 0) {
const width = template.panels[0].width
const height = template.panels[0].height
const paperType = state.paperTypes.find((a) => a.width == width && a.height == height)
state.curPaper = { type: paperType?.type || '', width: width, height: height }
if (state.curPaper.type === '') {
state.customPaper.width = width
state.customPaper.height = height
}
} else {
onSetPaper(state.curPaper.type)
}
}
/**
* 设置纸张大小
* @param type [A3, A4, A5, B3, B4, B5, '']
* @param value {width,height} mm
*/
const onSetPaper = (type: string, value?: { width: number; height: number }) => {
try {
const paperType = state.paperTypes.find((x) => x.type == type)
if (paperType) {
state.curPaper = { type: type, width: paperType.width, height: paperType.height }
hiprintTemplate.value.setPaper(paperType.width, paperType.height)
} else {
state.curPaper = { type: '', width: value?.width as number, height: value?.height as number }
hiprintTemplate.value.setPaper(value?.width, value?.height)
}
nextTick(() => {
printTemplateScrollbarRef.value.update()
})
} catch (error) {
proxy.$modal.msgError(`操作失败: ${error}`)
}
}
//
const onCustomPaper = () => {
popoverRef.value?.hide?.()
onSetPaper('', { width: state.customPaper.width, height: state.customPaper.height })
}
//
const onSetElsAlign = (e: any) => {
hiprintTemplate.value.setElsAlign(e)
}
//
const onChangeScale = () => {
if (hiprintTemplate.value) {
// scaleVal: , false: (), true,
hiprintTemplate.value.zoom(state.scaleValue)
}
}
//
const onRotatePaper = () => {
if (hiprintTemplate.value) {
hiprintTemplate.value.rotatePaper()
}
}
//
const onPreView = () => {
if (hiprintTemplate.value) {
previewRef.value.open(hiprintTemplate.value.getJson() || {}, JSON.parse(printData.value || '{}'), title.value)
}
}
//
const onClearPaper = () => {
proxy.$modal
.confirm(`确定要清空设计模板吗?`)
.then(async () => {
try {
if (hiprintTemplate.value) {
hiprintTemplate.value.clear()
}
} catch (error) {}
})
.catch(() => {})
}
//
const onPrint = async () => {
if (hiprintTemplate.value) {
hiprintTemplate.value.print(JSON.parse(printData.value || '{}'))
}
}
//Json
const onViewJson = () => {
if (hiprintTemplate.value) {
let templateJson = JSON.stringify(hiprintTemplate.value.getJson() || {})
previewJsonDialogRef.value.open(templateJson)
}
}
defineExpose({
hiprintTemplate,
setPaper,
})
</script>
<style lang="scss">
.hiprint-printElement {
color: #000;
}
</style>
<style scoped lang="scss">
.hiprint-printTemplate {
padding: 15px 10px 10px 15px;
}
:deep() {
.jsoneditor {
border-width: 0px;
}
.jsoneditor-menu {
color: var(--el-text-color-primary);
background-color: var(--el-color-primary);
border-bottom: 1px solid var(--el-border-color);
}
textarea.jsoneditor-text {
background-color: var(--el-bg-color-overlay);
color: var(--el-text-color-primary);
}
.hiprint-option-items {
padding: 10px;
}
.prop-tabs {
background-color: var(--el-bg-color);
border-style: none;
box-shadow: none;
border-color: var(--el-border-color);
}
.prop-tabs .prop-tab-items {
display: flex;
height: 40px;
padding: 0px;
border-bottom-color: var(--el-border-color);
}
.prop-tabs .prop-tab-items .prop-tab-item {
background-color: var(--el-bg-color);
flex: 1;
height: 40px;
line-height: 40px;
text-align: center;
}
.prop-tabs .prop-tab-items li.active {
border-bottom: 2px solid var(--el-color-primary);
color: var(--el-color-primary);
}
.prop-tabs .prop-tab-items .prop-tab-item .tab-title {
font-weight: normal;
}
.prop-tabs .hiprint-option-items {
background-color: var(--el-bg-color);
padding: 10px;
}
.hiprint-option-item-settingBtn {
width: 100%;
height: 30px;
line-height: 30px;
background-color: var(--el-color-primary);
}
.prop-tabs .hiprint-option-item-settingBtn {
width: 45%;
margin-left: 10px;
}
.hiprint-option-item-submitBtn {
background-color: var(--el-color-primary);
margin-bottom: 20px;
}
.hiprint-option-item-deleteBtn {
background-color: var(--el-color-danger);
margin-left: 10px;
margin-bottom: 20px;
}
.hiprint-option-items .hiprint-option-item-field input,
.hiprint-option-items .hiprint-option-item-field select,
.hiprint-option-items .hiprint-option-item-field textarea {
border: none;
background-color: var(--el-bg-color);
border-radius: var(--el-input-border-radius, var(--el-border-radius-base));
box-shadow: 0 0 0 1px var(--el-input-border-color, var(--el-border-color)) inset;
padding: 1px 11px;
height: calc(var(--el-component-size, 32px));
line-height: calc(var(--el-component-size, 32px) - 2px);
&:hover {
box-shadow: 0 0 0 1px var(--el-border-color-hover) inset;
}
&:focus {
box-shadow: 0 0 0 1px var(--el-color-primary) inset;
}
}
.left-box {
.el-tabs__header {
margin-bottom: 0px;
}
.el-tab-pane {
height: 100%;
}
}
.rect-printElement-types .hiprint-printElement-type a.ep-draggable-item {
height: auto;
color: #666;
box-shadow: none !important;
text-overflow: ellipsis;
}
.hiprint-printElement-type a.ep-draggable-item:before {
display: block;
width: 30px;
height: 30px;
content: '';
background-repeat: no-repeat;
background-size: contain;
margin: 0 auto;
}
.hiprint-printElement-type a.ep-draggable-item[tid='comModule.hline']:before {
background-image: url('/@/assets/svgs/hiprint/hline.svg');
}
.hiprint-printElement-type a.ep-draggable-item[tid='comModule.vline']:before {
background-image: url('/@/assets/svgs/hiprint/vline.svg');
}
.hiprint-printElement-type a.ep-draggable-item[tid='comModule.rect']:before {
background-image: url('/@/assets/svgs/hiprint/rect.svg');
}
.hiprint-printElement-type a.ep-draggable-item[tid='comModule.oval']:before {
background-image: url('/@/assets/svgs/hiprint/oval.svg');
}
.hiprint-printElement-type a.ep-draggable-item[tid='comModule.barcode']:before {
background-image: url('/@/assets/svgs/hiprint/barcode.svg');
}
.hiprint-printElement-type a.ep-draggable-item[tid='comModule.qrcode']:before {
background-image: url('/@/assets/svgs/hiprint/qrcode.svg');
}
.hiprint-printElement-type a.ep-draggable-item[tid='comModule.text']:before {
background-image: url('/@/assets/svgs/hiprint/text.svg');
}
.hiprint-printElement-type a.ep-draggable-item[tid='comModule.longText']:before {
background-image: url('/@/assets/svgs/hiprint/longText.svg');
}
.hiprint-printElement-type a.ep-draggable-item[tid='comModule.table']:before {
background-image: url('/@/assets/svgs/hiprint/table.svg');
}
.hiprint-printElement-type a.ep-draggable-item[tid='comModule.emptyTable']:before {
background-image: url('/@/assets/svgs/hiprint/emptyTable.svg');
}
.hiprint-printElement-type a.ep-draggable-item[tid='comModule.html']:before {
background-image: url('/@/assets/svgs/hiprint/html.svg');
}
.hiprint-printElement-type a.ep-draggable-item[tid='comModule.image']:before {
background-image: url('/@/assets/svgs/hiprint/image.svg');
}
}
</style>

View File

@ -1,122 +0,0 @@
<template>
<div>
<el-dialog
v-model="state.showDialog"
destroy-on-close
:title="title"
draggable
:close-on-click-modal="false"
:close-on-press-escape="false"
width="600px"
>
<el-form :model="form" ref="formRef" label-width="80px">
<el-row :gutter="35">
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="模板名称" prop="name" :rules="[{ required: true, message: '请输入模板名称', trigger: ['blur', 'change'] }]">
<el-input v-model="form.name" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="模板编码" prop="code" :rules="[{ required: true, message: '请输入模板编码', trigger: ['blur', 'change'] }]">
<el-input v-model="form.code" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="排序">
<el-input-number v-model="form.sort" class="w100" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="启用">
<el-switch v-model="form.enabled" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel"> </el-button>
<el-button type="primary" @click="onSure" :loading="state.sureLoading"> </el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup name="admin/print-template-form">
import { reactive, toRefs, ref } from 'vue'
import { PrintTemplateUpdateInput, PrintTemplateGetOutput } from '/@/api/admin/data-contracts'
import { PrintTemplateApi } from '/@/api/admin/PrintTemplate'
import eventBus from '/@/utils/mitt'
defineProps({
title: {
type: String,
default: '',
},
})
const formRef = ref()
const state = reactive({
showDialog: false,
sureLoading: false,
form: {
enabled: true,
} as PrintTemplateUpdateInput & PrintTemplateGetOutput,
})
const { form } = toRefs(state)
//
const open = async (row: any = {}) => {
if (row.id > 0) {
const res = await new PrintTemplateApi().get({ id: row.id }, { loading: true })
if (res?.success) {
let formData = res.data as PrintTemplateUpdateInput & PrintTemplateGetOutput
state.form = formData
}
} else {
state.form = {
enabled: true,
} as PrintTemplateUpdateInput & PrintTemplateGetOutput
}
state.showDialog = true
}
//
const onCancel = () => {
state.showDialog = false
}
//
const onSure = () => {
formRef.value.validate(async (valid: boolean) => {
if (!valid) return
state.sureLoading = true
let res = {} as any
if (state.form.id != undefined && state.form.id > 0) {
res = await new PrintTemplateApi().update(state.form, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
} else {
res = await new PrintTemplateApi().add(state.form, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
}
state.sureLoading = false
if (res?.success) {
eventBus.emit('refreshPrintTemplate')
state.showDialog = false
}
})
}
defineExpose({
open,
})
</script>

View File

@ -1,119 +0,0 @@
<template>
<div>
<el-dialog
v-model="state.showDialog"
destroy-on-close
:title="state.title"
draggable
:close-on-click-modal="false"
:close-on-press-escape="true"
:width="width"
:show-close="false"
style="max-width: 90%"
body-class="preview-box"
>
<template #header="{ titleId, titleClass }">
<div class="my-flex my-flex-between mr10">
<span :id="titleId" :class="titleClass">{{ state.title }}</span>
<div>
<el-button type="primary" @click="onExport">
<template #icon>
<el-icon>
<my-icon name="export" color="var(--color)"></my-icon>
</el-icon>
</template>
导出PDF
</el-button>
<el-button type="primary" icon="ele-Printer" @click="onPrint"> </el-button>
<el-button @click="onCancel"> </el-button>
</div>
</div>
</template>
<div ref="previewContainerRef"></div>
</el-dialog>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref, nextTick, computed } from 'vue'
import { hiprint } from 'vue-plugin-hiprint'
const state = reactive({
showDialog: false,
width: 0,
title: '',
printData: {},
template: {} as any,
})
const width = computed(() => {
return `${state.width + 20}mm`
})
const hiprintTemplate = ref()
const previewContainerRef = ref<HTMLElement | null>(null) //
//
const open = async (template: any, printData: {}, title = '打印模板') => {
state.title = title
state.template = template
state.printData = printData
hiprintTemplate.value = new hiprint.PrintTemplate({
template: state.template,
})
state.width = hiprintTemplate.value.printPanels?.length > 0 ? hiprintTemplate.value.printPanels[0].width : 210
const htmlElements = hiprintTemplate.value.getHtml(printData)
state.showDialog = true
await nextTick()
//
if (previewContainerRef.value) {
previewContainerRef.value.innerHTML = ''
}
// HTML
if (htmlElements?.length > 0 && previewContainerRef.value) {
htmlElements.each((index: number, element: HTMLElement) => {
previewContainerRef.value?.appendChild(element)
})
}
}
// PDF
const onExport = () => {
hiprintTemplate.value.toPdf(state.printData, state.title)
}
//
const onPrint = async () => {
if (hiprintTemplate.value) {
hiprintTemplate.value.print(state.printData)
}
}
//
const onCancel = () => {
state.showDialog = false
}
defineExpose({
open,
})
</script>
<style scoped lang="scss">
:deep() {
.el-dialog__body {
overflow-x: auto;
}
.preview-box {
background-color: var(--el-bg-color-overlay);
}
.hiprint-printPaper {
background-color: var(--el-color-white);
}
}
</style>

View File

@ -1,165 +0,0 @@
import { hiprint } from 'vue-plugin-hiprint'
import logoImg from '/@/assets/logo-mini.svg'
export const comProvider = function () {
var addElementTypes = function (context: any) {
context.removePrintElementTypes('comModule')
context.addPrintElementTypes('comModule', [
new hiprint.PrintElementTypeGroup('常用组件', [
{
tid: 'comModule.text',
title: '文本',
data: '',
type: 'text',
},
{
tid: 'comModule.longText',
title: '长文本',
data: '',
type: 'longText',
options: {
width: 500,
},
},
{
tid: 'comModule.table',
field: 'table',
title: '表格',
type: 'table',
groupFields: ['name'],
groupFooterFormatter: function (group: any, option: any) {
return '这里自定义统计脚信息'
},
options: {
width: 500,
},
columns: [
[
{
title: '行号',
fixed: true,
rowspan: 2,
field: 'id',
width: 70,
},
{ title: '人员信息', colspan: 2 },
{ title: '销售统计', colspan: 2 },
],
[
{
title: '姓名',
align: 'left',
field: 'name',
width: 100,
},
{ title: '性别', field: 'gender', width: 100 },
{
title: '销售数量',
field: 'count',
width: 100,
},
{
title: '销售金额',
field: 'amount',
width: 100,
},
],
],
editable: true,
columnDisplayEditable: true, //列显示是否能编辑
columnDisplayIndexEditable: true, //列顺序显示是否能编辑
columnTitleEditable: true, //列标题是否能编辑
columnResizable: true, //列宽是否能调整
columnAlignEditable: true, //列对齐是否调整
isEnableEditField: true, //编辑字段
isEnableContextMenu: true, //开启右键菜单 默认true
isEnableInsertRow: true, //插入行
isEnableDeleteRow: true, //删除行
isEnableInsertColumn: true, //插入列
isEnableDeleteColumn: true, //删除列
isEnableMergeCell: true, //合并单元格
},
{
tid: 'comModule.emptyTable',
title: '空白表格',
type: 'table',
options: {
width: 500,
},
columns: [
[
{
title: '',
field: '',
width: 100,
},
{
title: '',
field: '',
width: 100,
},
],
],
},
{
tid: 'comModule.html',
title: 'html',
formatter: function (data: any, options: any) {
return '<div style="height:50pt;width:50pt;background:red;border-radius: 50%;"></div>'
},
type: 'html',
},
{
tid: 'comModule.image',
title: '图片',
type: 'image',
options: { field: '', src: logoImg },
},
]),
new hiprint.PrintElementTypeGroup('辅助组件', [
{
tid: 'comModule.hline',
title: '横线',
type: 'hline',
},
{
tid: 'comModule.vline',
title: '竖线',
type: 'vline',
},
{
tid: 'comModule.rect',
title: '矩形',
type: 'rect',
},
{
tid: 'comModule.oval',
title: '椭圆',
type: 'oval',
},
{
tid: 'comModule.barcode',
title: '条形码',
type: 'barcode',
},
{
tid: 'comModule.qrcode',
title: '二维码',
type: 'qrcode',
},
]),
])
}
return {
addElementTypes: addElementTypes,
}
}
export default [
{
name: '常用组件',
value: 'comModule',
type: 1,
f: comProvider(),
},
]

View File

@ -1,67 +0,0 @@
<template>
<el-drawer v-model="state.showDialog" direction="rtl" destroy-on-close :size="size">
<template #header="{ titleId, titleClass }">
<h4 :id="titleId" :class="titleClass">{{ title }}</h4>
<el-icon v-if="state.isFull" class="el-drawer__btn" @click="state.isFull = !state.isFull" title="还原"><ele-CopyDocument /></el-icon>
<el-icon v-else class="el-drawer__btn" @click="state.isFull = !state.isFull" title="最大化"><ele-FullScreen /></el-icon>
</template>
<div class="my-fill h100">
<MyJsonEditor
v-model="state.templateJson"
:options="{
mainMenuBar: false,
statusBar: false,
onEditable: () => false,
}"
></MyJsonEditor>
</div>
</el-drawer>
</template>
<script lang="ts" setup>
import { reactive, computed } from 'vue'
import MyJsonEditor from '/@/components/my-json-editor/index.vue'
defineProps({
title: {
type: String,
default: '查看JSON',
},
})
const state = reactive({
showDialog: false,
isFull: false,
isMobile: document.body.clientWidth < 1000,
templateJson: '',
})
const size = computed(() => {
return state.isMobile ? '100%' : state.isFull ? '100%' : '45%'
})
//
const open = (templateJson: any) => {
state.templateJson = templateJson
state.showDialog = true
}
defineExpose({
open,
})
</script>
<style scoped lang="scss">
.el-alert {
border-width: 0px !important;
margin-left: 110px;
margin-top: 10px;
}
.el-drawer__btn {
cursor: pointer;
margin-right: 8px;
&:hover {
color: var(--el-color-primary);
}
}
</style>

View File

@ -1,201 +0,0 @@
<template>
<my-layout>
<el-card class="my-query-box mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
<el-form ref="filterFormRef" :model="state.filter" :inline="true" label-width="auto" :label-position="'left'" @submit.stop.prevent>
<el-form-item label="模板名称" prop="name">
<el-input v-model="state.filter.name" placeholder="模板名称" @keyup.enter="onQuery" />
</el-form-item>
<el-form-item label="模板编码" prop="code">
<el-input v-model="state.filter.code" placeholder="模板编码" @keyup.enter="onQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="ele-Search" @click="onQuery"> 查询 </el-button>
<el-button icon="ele-RefreshLeft" text bg @click="onReset"> 重置 </el-button>
<el-button v-if="auth('api:admin:print-template:add')" type="primary" icon="ele-Plus" @click="onAdd"> 新增 </el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="my-fill mt8" shadow="never">
<el-table v-loading="state.loading" :data="state.dataList" default-expand-all highlight-current-row style="width: 100%" border>
<el-table-column prop="name" label="模板名称" min-width="120" show-overflow-tooltip />
<el-table-column prop="code" label="模板编码" min-width="120" show-overflow-tooltip />
<el-table-column prop="sort" label="排序" width="82" align="center" show-overflow-tooltip />
<el-table-column label="状态" width="88" align="center" fixed="right">
<template #default="{ row }">
<el-switch
v-if="auth('api:admin:print-template:set-enable')"
v-model="row.enabled"
:loading="row.loading"
:active-value="true"
:inactive-value="false"
inline-prompt
active-text="启用"
inactive-text="禁用"
:before-change="() => onSetEnable(row)"
/>
<template v-else>
<el-tag type="success" v-if="row.enabled">启用</el-tag>
<el-tag type="danger" v-else>禁用</el-tag>
</template>
</template>
</el-table-column>
<el-table-column label="操作" width="160" fixed="right" header-align="center" align="center">
<template #default="{ row }">
<el-button v-auth="'api:admin:print-template:update'" text type="primary" @click="onEdit(row)"> 编辑 </el-button>
<el-button v-auth="'api:admin:print-template:design'" text type="primary" @click="onDesign(row)"> 设计 </el-button>
<el-button v-auth="'api:admin:print-template:delete'" text type="danger" @click="onDelete(row)"> 删除 </el-button>
</template>
</el-table-column>
</el-table>
<div class="my-flex my-flex-end" style="margin-top: 10px">
<el-pagination
v-model:currentPage="state.pageInput.currentPage"
v-model:page-size="state.pageInput.pageSize"
:total="state.total"
:page-sizes="[10, 20, 50, 100]"
background
@size-change="onSizeChange"
@current-change="onCurrentChange"
layout="total, sizes, prev, pager, next, jumper"
/>
</div>
</el-card>
<PrintTemplateForm ref="formRef" :title="state.formTitle"></PrintTemplateForm>
<PrintTemplateDesignDialog ref="designDialogRef" :title="state.designTitle"></PrintTemplateDesignDialog>
</my-layout>
</template>
<script lang="ts" setup name="admin/print-template">
import { ref, reactive, onMounted, getCurrentInstance, onBeforeMount, defineAsyncComponent } from 'vue'
import { PageInputPrintTemplateGetPageInput, PrintTemplateGetPageOutput } from '/@/api/admin/data-contracts'
import { PrintTemplateApi } from '/@/api/admin/PrintTemplate'
import eventBus from '/@/utils/mitt'
import { auth } from '/@/utils/authFunction'
import type { FormInstance } from 'element-plus'
//
const PrintTemplateForm = defineAsyncComponent(() => import('./components/form.vue'))
const PrintTemplateDesignDialog = defineAsyncComponent(() => import('./components/design-dialog.vue'))
const { proxy } = getCurrentInstance() as any
const filterFormRef = ref<FormInstance>()
const formRef = ref()
const designDialogRef = ref()
const state = reactive({
loading: false,
formTitle: '',
designTitle: '',
total: 0,
filter: {
name: '',
code: '',
},
pageInput: {
currentPage: 1,
pageSize: 20,
} as PageInputPrintTemplateGetPageInput,
dataList: [] as Array<PrintTemplateGetPageOutput>,
})
onMounted(async () => {
await onQuery()
eventBus.off('refreshPrintTemplate')
eventBus.on('refreshPrintTemplate', async () => {
onQuery()
})
})
onBeforeMount(() => {
eventBus.off('refreshPrintTemplate')
})
const onSizeChange = (val: number) => {
state.pageInput.currentPage = 1
state.pageInput.pageSize = val
onQuery()
}
const onCurrentChange = (val: number) => {
state.pageInput.currentPage = val
onQuery()
}
const onQuery = async () => {
state.loading = true
state.pageInput.filter = state.filter
const res = await new PrintTemplateApi().getPage(state.pageInput).catch(() => {
state.loading = false
})
state.dataList = res?.data?.list ?? []
state.total = res?.data?.total ?? 0
state.loading = false
}
const onReset = () => {
filterFormRef.value!.resetFields()
onQuery()
}
const onAdd = () => {
state.formTitle = '新增打印模板'
formRef.value.open()
}
const onEdit = (row: PrintTemplateGetPageOutput) => {
state.formTitle = '编辑打印模板'
formRef.value.open(row)
}
const onDesign = (row: PrintTemplateGetPageOutput) => {
state.designTitle = row.name ? row.name : '设计打印模板'
designDialogRef.value.open(row)
}
const onDelete = (row: PrintTemplateGetPageOutput) => {
proxy.$modal
.confirmDelete(`确定要删除打印模板【${row.name}】?`)
.then(async () => {
await new PrintTemplateApi().delete({ id: row.id }, { loading: true })
onQuery()
})
.catch(() => {})
}
//
const onSetEnable = (row: PrintTemplateGetPageOutput & { loading: boolean }) => {
return new Promise((resolve, reject) => {
proxy.$modal
.confirm(`确定要${row.enabled ? '禁用' : '启用'}${row.name}】?`)
.then(async () => {
row.loading = true
const res = await new PrintTemplateApi()
.setEnable({ printTemplateId: row.id, enabled: !row.enabled }, { showSuccessMessage: true })
.catch(() => {
reject(new Error('Error'))
})
.finally(() => {
row.loading = false
})
if (res && res.success) {
resolve(true)
onQuery()
} else {
reject(new Error('Cancel'))
}
})
.catch(() => {
reject(new Error('Cancel'))
})
})
}
</script>
<style scoped lang="scss"></style>

View File

@ -1,193 +0,0 @@
<template>
<div>
<el-form ref="formRef" :model="form" size="large" class="login-content-form">
<div class="login-title">注册账号</div>
<el-form-item
class="login-animation1"
prop="email"
:rules="[
{ required: true, message: '请输入邮箱地址', trigger: ['blur', 'change'] },
{ validator: testEmail, trigger: ['blur', 'change'] },
]"
>
<el-input ref="emailRef" text :placeholder="$t('message.email.placeholder1')" v-model="form.email" clearable autocomplete="off">
<template #prefix>
<el-icon class="el-input__icon"><ele-Promotion /></el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item class="login-animation2" prop="code" :rules="[{ required: true, message: '请输入邮箱验证码', trigger: ['blur', 'change'] }]">
<MyInputCode v-model="form.code" :email="form.email" :validate="validatorEmail" @send="onSend" />
</el-form-item>
<el-form-item
v-if="hasPassword"
class="login-animation3"
prop="password"
:rules="[
{ required: true, message: '请输入密码', trigger: ['blur', 'change'] },
{ validator: validatorPwd, trigger: ['blur', 'change'] },
]"
>
<el-input v-model="form.password" :placeholder="'输入密码'" show-password autocomplete="off" clearable @input="onInputPassword">
<template #prefix>
<el-icon class="el-input__icon"><ele-Unlock /></el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item
class="login-animation3 mb10"
prop="corpName"
:rules="[{ required: true, message: '请填写完整企业名称', trigger: ['blur', 'change'] }]"
>
<el-input ref="emailRef" text :placeholder="$t('请填写完整企业名称')" v-model="form.corpName" clearable autocomplete="off">
<template #prefix>
<el-icon class="el-input__icon"><ele-OfficeBuilding /></el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item class="login-animation4 mb5">
<el-button round type="primary" v-waves class="login-content-submit" :loading="state.loading" @click="onReg">
<span>{{ $t('注册') }}</span>
</el-button>
</el-form-item>
<el-form-item class="login-animation5 mb5 login-agree" prop="agree" :rules="[{ validator: validatorAgree, trigger: ['change'] }]">
<div class="my-flex my-flex-items-center f12">
<el-checkbox v-model="form.agree">我已阅读并同意</el-checkbox>
<div class="my-flex my-flex-items-center ml5">
<el-link underline="never" type="primary" class="f12" target="_blank" href="https://zhontai.net/admin/introduce.html">服务协议</el-link>
<el-link underline="never" type="primary" class="f12" target="_blank" href="https://zhontai.net/admin/introduce.html">隐私政策</el-link>
</div>
</div>
</el-form-item>
</el-form>
</div>
</template>
<script lang="ts" setup name="admin/reg-email">
import { reactive, toRefs, ref, defineAsyncComponent } from 'vue'
import { AuthRegByEmailInput } from '/@/api/admin/data-contracts'
import { AuthApi } from '/@/api/admin/Auth'
import { verifyCnAndSpace } from '/@/utils/toolsValidate'
import { validatorPwd, validatorAgree } from '/@/utils/validators'
import { testEmail } from '/@/utils/test'
import { ElMessage } from 'element-plus'
const MyInputCode = defineAsyncComponent(() => import('/@/components/my-input-code/index.vue'))
const isReg = defineModel('isReg', { type: Boolean, default: false })
const hasPassword = defineModel('hasPassword', { type: Boolean, default: false })
const formRef = ref()
const emailRef = ref()
const state = reactive({
showDialog: false,
loading: false,
form: {
agree: false,
email: '',
code: '',
codeId: '',
password: '',
corpName: '中台',
} as AuthRegByEmailInput & { agree: false },
})
const { form } = toRefs(state)
//
const validatorEmail = (callback: Function) => {
formRef.value.validateField('email', (isValid: boolean) => {
if (!isValid) {
emailRef.value?.focus()
return
}
callback?.()
})
}
//
const onSend = (codeId: string) => {
state.form.codeId = codeId
}
//
const onInputPassword = (val: string) => {
state.form.password = verifyCnAndSpace(val)
}
//
const open = async () => {
state.showDialog = true
state.form = {} as AuthRegByEmailInput & { agree: false }
}
//
const onReg = () => {
formRef.value.validate(async (valid: boolean) => {
if (!valid) return
state.loading = true
const res = await new AuthApi().regByEmail(state.form, { showSuccessMessage: false }).catch(() => {
state.loading = false
})
state.loading = false
if (res?.success) {
ElMessage.success('注册成功')
isReg.value = false
}
})
}
defineExpose({
open,
})
</script>
<style scoped lang="scss">
.login-content-form {
.login-title {
margin-bottom: 50px;
font-size: 27px;
text-align: center;
letter-spacing: 3px;
color: var(--el-text-color-primary);
position: relative;
}
.login-remind {
color: #7f8792;
margin-right: 5px;
}
@for $i from 1 through 5 {
.login-animation#{$i} {
opacity: 0;
animation-name: error-num;
animation-duration: 0.5s;
animation-fill-mode: forwards;
animation-delay: calc($i/10) + s;
}
}
.login-content-code {
width: 100%;
padding: 0;
}
.login-content-submit {
width: 100%;
letter-spacing: 2px;
font-weight: 300;
margin-top: 15px;
}
.login-msg {
color: var(--el-text-color-placeholder);
}
.f12 {
font-size: 12px;
}
:deep() {
.el-checkbox__input.is-checked + .el-checkbox__label {
color: unset;
}
.login-agree.el-form-item--large .el-form-item__content {
line-height: unset !important;
}
}
}
</style>

View File

@ -1,201 +0,0 @@
<template>
<div>
<el-form ref="formRef" :model="form" size="large" class="login-content-form">
<div class="login-title">注册账号</div>
<el-form-item
class="login-animation1"
prop="mobile"
:rules="[
{ required: true, message: '请输入手机号', trigger: ['blur', 'change'] },
{ validator: testMobile, trigger: ['blur', 'change'] },
]"
>
<el-input
ref="phoneRef"
text
:placeholder="$t('message.mobile.placeholder1')"
maxlength="11"
v-model="form.mobile"
clearable
autocomplete="off"
>
<template #prefix>
<el-icon class="el-input__icon"><ele-Iphone /></el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item class="login-animation2" prop="code" :rules="[{ required: true, message: '请输入短信验证码', trigger: ['blur', 'change'] }]">
<MyInputCode v-model="form.code" :mobile="form.mobile" :validate="validatorMobile" @send="onSend" />
</el-form-item>
<el-form-item
v-if="hasPassword"
class="login-animation3"
prop="password"
:rules="[
{ required: true, message: '请输入密码', trigger: ['blur', 'change'] },
{ validator: validatorPwd, trigger: ['blur', 'change'] },
]"
>
<el-input v-model="form.password" :placeholder="'输入密码'" show-password autocomplete="off" clearable @input="onInputPassword">
<template #prefix>
<el-icon class="el-input__icon"><ele-Unlock /></el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item
class="login-animation3 mb10"
prop="corpName"
:rules="[{ required: true, message: '请填写完整企业名称', trigger: ['blur', 'change'] }]"
>
<el-input ref="emailRef" text :placeholder="$t('请填写完整企业名称')" v-model="form.corpName" clearable autocomplete="off">
<template #prefix>
<el-icon class="el-input__icon"><ele-OfficeBuilding /></el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item class="login-animation4 mb5">
<el-button round type="primary" v-waves class="login-content-submit" :loading="state.loading" @click="onReg">
<span>{{ $t('注册') }}</span>
</el-button>
</el-form-item>
<el-form-item class="login-animation5 mb5 login-agree" prop="agree" :rules="[{ validator: validatorAgree, trigger: ['change'] }]">
<div class="my-flex my-flex-items-center f12">
<el-checkbox v-model="form.agree">我已阅读并同意</el-checkbox>
<div class="my-flex my-flex-items-center ml5">
<el-link underline="never" type="primary" class="f12" target="_blank" href="https://zhontai.net/admin/introduce.html">服务协议</el-link>
<el-link underline="never" type="primary" class="f12" target="_blank" href="https://zhontai.net/admin/introduce.html">隐私政策</el-link>
</div>
</div>
</el-form-item>
</el-form>
</div>
</template>
<script lang="ts" setup name="admin/reg-email">
import { reactive, toRefs, ref, defineAsyncComponent } from 'vue'
import { AuthRegByMobileInput } from '/@/api/admin/data-contracts'
import { AuthApi } from '/@/api/admin/Auth'
import { verifyCnAndSpace } from '/@/utils/toolsValidate'
import { validatorPwd, validatorAgree } from '/@/utils/validators'
import { testMobile } from '/@/utils/test'
import { ElMessage } from 'element-plus'
const MyInputCode = defineAsyncComponent(() => import('/@/components/my-input-code/index.vue'))
const isReg = defineModel('isReg', { type: Boolean, default: false })
const hasPassword = defineModel('hasPassword', { type: Boolean, default: false })
const formRef = ref()
const phoneRef = ref()
const state = reactive({
showDialog: false,
loading: false,
form: {
agree: false,
mobile: '',
code: '',
codeId: '',
password: '',
corpName: '中台',
} as AuthRegByMobileInput & { agree: false },
})
const { form } = toRefs(state)
//
const validatorMobile = (callback: Function) => {
formRef.value.validateField('mobile', (isValid: boolean) => {
if (!isValid) {
phoneRef.value?.focus()
return
}
callback?.()
})
}
//
const onSend = (codeId: string) => {
state.form.codeId = codeId
}
//
const onInputPassword = (val: string) => {
state.form.password = verifyCnAndSpace(val)
}
//
const open = async () => {
state.showDialog = true
state.form = {} as AuthRegByMobileInput & { agree: false }
}
//
const onReg = () => {
formRef.value.validate(async (valid: boolean) => {
if (!valid) return
state.loading = true
const res = await new AuthApi().regByMobile(state.form, { showSuccessMessage: false }).catch(() => {
state.loading = false
})
state.loading = false
if (res?.success) {
ElMessage.success('注册成功')
isReg.value = false
}
})
}
defineExpose({
open,
})
</script>
<style scoped lang="scss">
.login-content-form {
.login-title {
margin-bottom: 50px;
font-size: 27px;
text-align: center;
letter-spacing: 3px;
color: var(--el-text-color-primary);
position: relative;
}
.login-remind {
color: #7f8792;
margin-right: 5px;
}
@for $i from 1 through 5 {
.login-animation#{$i} {
opacity: 0;
animation-name: error-num;
animation-duration: 0.5s;
animation-fill-mode: forwards;
animation-delay: calc($i/10) + s;
}
}
.login-content-code {
width: 100%;
padding: 0;
}
.login-content-submit {
width: 100%;
letter-spacing: 2px;
font-weight: 300;
margin-top: 15px;
}
.login-msg {
color: var(--el-text-color-placeholder);
}
.f12 {
font-size: 12px;
}
:deep() {
.el-checkbox__input.is-checked + .el-checkbox__label {
color: unset;
}
.login-agree.el-form-item--large .el-form-item__content {
line-height: unset !important;
}
}
}
</style>

View File

@ -1,183 +0,0 @@
<template>
<div>
<el-dialog
v-model="state.showDialog"
destroy-on-close
:title="title"
draggable
:close-on-click-modal="false"
:close-on-press-escape="false"
width="600px"
>
<el-form :model="form" ref="formRef" label-width="80px">
<el-row :gutter="35">
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="上级地区">
<RegionSelect v-model="form.parentIdList" v-model:parentId="form.parentId" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="类型" prop="level" :rules="[{ required: true, message: '请选择类型', trigger: ['change'] }]">
<el-select v-model="form.level" placeholder="请选择类型" class="w100">
<el-option v-for="item in state.regionLevelList" :key="item.label" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="地区名称" prop="name" :rules="[{ required: true, message: '请输入地区名称', trigger: ['blur', 'change'] }]">
<el-input v-model="form.name" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="地区简称">
<el-input v-model="form.shortName" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="地区代码" prop="code" :rules="[{ required: true, message: '请输入地区代码', trigger: ['blur', 'change'] }]">
<el-input v-model="form.code" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="驻地">
<el-input v-model="form.capital" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="人口">
<el-input v-model="form.population" clearable>
<template #append>万人</template>
</el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="面积">
<el-input v-model="form.area" clearable>
<template #append>km²</template>
</el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="区号">
<el-input v-model="form.areaCode" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="邮编">
<el-input v-model="form.zipCode" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="热门">
<el-switch v-model="form.hot" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="排序">
<el-input-number v-model="form.sort" class="w100" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="启用">
<el-switch v-model="form.enabled" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel"> </el-button>
<el-button type="primary" @click="onSure" :loading="state.sureLoading"> </el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup>
import { reactive, toRefs, ref, defineAsyncComponent } from 'vue'
import { RegionUpdateInput, RegionGetOutput } from '/@/api/admin/data-contracts'
import { RegionApi } from '/@/api/admin/Region'
import eventBus from '/@/utils/mitt'
import { RegionLevel as RegionLevelEnum } from '/@/api/admin/enum-contracts'
import { toOptionsByValue } from '/@/utils/enum'
const RegionSelect = defineAsyncComponent(() => import('./region-select.vue'))
defineProps({
title: {
type: String,
default: '',
},
})
const formRef = ref()
const state = reactive({
showDialog: false,
sureLoading: false,
regionLevelList: toOptionsByValue(RegionLevelEnum),
form: {
enabled: true,
hot: false,
} as RegionUpdateInput & RegionGetOutput,
})
const { form } = toRefs(state)
//
const open = async (row: any = {}) => {
if (row.id > 0) {
const res = await new RegionApi().get({ id: row.id }, { loading: true })
if (res?.success) {
let formData = res.data as RegionUpdateInput & RegionGetOutput
formData.parentId = formData.parentId && formData.parentId > 0 ? formData.parentId : undefined
state.form = formData
}
} else {
state.form = {
enabled: true,
hot: false,
} as RegionUpdateInput & RegionGetOutput
}
state.showDialog = true
}
//
const onCancel = () => {
state.showDialog = false
}
//
const onSure = () => {
formRef.value.validate(async (valid: boolean) => {
if (!valid) return
state.sureLoading = true
let res = {} as any
state.form.parentId = state.form.parentId && state.form.parentId > 0 ? state.form.parentId : undefined
if (state.form.id != undefined && state.form.id > 0) {
res = await new RegionApi().update(state.form, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
} else {
res = await new RegionApi().add(state.form, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
}
state.sureLoading = false
if (res?.success) {
eventBus.emit('refreshRegion')
state.showDialog = false
}
})
}
defineExpose({
open,
})
</script>

View File

@ -1,59 +0,0 @@
<template>
<el-cascader
placeholder="请选择上级地区"
:options="state.regionList"
:props="cascaderProps"
:persistent="true"
filterable
clearable
class="w100"
@change="onChange"
v-bind="$attrs"
><template #default="{ data }">
<span>{{ data.name }}</span>
<my-icon v-if="data.hot" name="hot" color="#ea322b" size="12" class="ml5"></my-icon>
</template>
</el-cascader>
</template>
<script lang="ts" setup>
import { reactive } from 'vue'
import type { CascaderProps } from 'element-plus'
import { RegionApi } from '/@/api/admin/Region'
const parentId = defineModel('parentId', { type: Number, default: undefined })
const state = reactive({
regionList: [] as any,
})
const cascaderProps: CascaderProps = {
checkStrictly: true,
value: 'id',
label: 'name',
lazy: true,
lazyLoad(node, resolve) {
const value = node.value as number
new RegionApi()
.getChildList({ parentId: value > 0 ? value : 0 })
.then((r) => {
resolve(r.data as any)
})
.catch(() => {
resolve([])
})
},
}
const onChange = (value: any) => {
parentId.value = value && value.length > 0 ? value[value.length - 1] : undefined
}
const reset = () => {
state.regionList = []
}
defineExpose({
reset,
})
</script>

View File

@ -1,330 +0,0 @@
<template>
<my-layout>
<el-card class="my-query-box mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
<el-form ref="filterFormRef" :model="state.filter" :inline="true" label-width="auto" :label-position="'left'" @submit.stop.prevent>
<el-form-item label="" prop="parentId">
<RegionSelect ref="regionSelectRef" v-model:parentId="state.filter.parentId" placeholder="上级地区" />
</el-form-item>
<el-form-item label="" prop="name">
<el-input v-model="state.filter.name" placeholder="地区名" @keyup.enter="onQuery" />
</el-form-item>
<el-form-item label="类型" prop="level">
<el-select v-model="state.filter.level" empty-values="[null]" style="width: 100px" @change="onQuery">
<el-option label="全部" :value="undefined" />
<el-option v-for="item in state.regionLevelList" :key="item.label" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="状态" prop="enabled">
<el-select v-model="state.filter.enabled" :empty-values="[null]" style="width: 100px" @change="onQuery">
<el-option v-for="item in state.statusList" :key="item.name" :label="item.name" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="热门" prop="hot">
<el-select v-model="state.filter.hot" :empty-values="[null]" style="width: 100px" @change="onQuery">
<el-option v-for="item in state.hotList" :key="item.name" :label="item.name" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="ele-Search" @click="onQuery"> 查询 </el-button>
<el-button icon="ele-RefreshLeft" text bg @click="onReset"> 重置 </el-button>
<el-button v-if="auth('api:admin:region:add')" type="primary" icon="ele-Plus" @click="onAdd"> 新增 </el-button>
<el-button v-if="auth('api:admin:region:sync-data')" ref="syncRef" :loading="state.sync.loading" type="primary" icon="ele-Refresh">
同步
</el-button>
<el-popover
v-if="auth('api:admin:region:sync-data')"
ref="popoverRef"
:virtual-ref="syncRef"
trigger="click"
virtual-triggering
:width="300"
>
<p class="my-flex my-flex-items-center">
确定要同步数据
<!-- 确定要同步至
<el-select v-model="state.sync.regionLevel" :teleported="false" style="width: 75px; margin: 0px 5px">
<el-option v-for="item in state.regionLevelList" :key="item.label" :label="item.label" :value="item.value" />
</el-select>
-->
</p>
<div class="mt10" style="text-align: right">
<el-button text @click="onSyncCancel">取消</el-button>
<el-button type="primary" @click="onSync"> 确定 </el-button>
</div>
</el-popover>
</el-form-item>
</el-form>
</el-card>
<el-card class="my-fill mt8" shadow="never">
<el-table v-loading="state.loading" :data="state.dataList" default-expand-all highlight-current-row style="width: 100%" border>
<el-table-column prop="name" label="地区名" min-width="120" show-overflow-tooltip />
<el-table-column prop="code" label="代码" min-width="120" show-overflow-tooltip />
<el-table-column prop="level" label="类型" min-width="140" show-overflow-tooltip :formatter="formatterEnum" />
<el-table-column prop="pinyin" label="拼音" min-width="120" show-overflow-tooltip />
<el-table-column prop="sort" label="排序" width="82" align="center" show-overflow-tooltip />
<el-table-column label="状态" width="88" align="center" fixed="right">
<template #default="{ row }">
<el-switch
v-if="auth('api:admin:region:set-enable')"
v-model="row.enabled"
:loading="row.loading"
:active-value="true"
:inactive-value="false"
inline-prompt
active-text="启用"
inactive-text="禁用"
:before-change="() => onSetEnable(row)"
/>
<template v-else>
<el-tag type="success" v-if="row.enabled">启用</el-tag>
<el-tag type="danger" v-else>禁用</el-tag>
</template>
</template>
</el-table-column>
<el-table-column label="热门" width="88" align="center" fixed="right">
<template #default="{ row }">
<el-switch
v-if="auth('api:admin:region:set-hot')"
v-model="row.hot"
:loading="row.hotLoading"
:active-value="true"
:inactive-value="false"
inline-prompt
active-text="是"
inactive-text="否"
:before-change="() => onSetHot(row)"
/>
<template v-else>
<el-tag type="success" v-if="row.enabled"></el-tag>
<el-tag type="danger" v-else></el-tag>
</template>
</template>
</el-table-column>
<el-table-column label="操作" width="160" fixed="right" header-align="center" align="center">
<template #default="{ row }">
<el-button v-auth="'api:admin:region:update'" icon="ele-EditPen" text type="primary" @click="onEdit(row)">编辑</el-button>
<el-button v-auth="'api:admin:region:delete'" icon="ele-Delete" text type="danger" @click="onDelete(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="my-flex my-flex-end" style="margin-top: 10px">
<el-pagination
v-model:currentPage="state.pageInput.currentPage"
v-model:page-size="state.pageInput.pageSize"
:total="state.total"
:page-sizes="[10, 20, 50, 100]"
background
@size-change="onSizeChange"
@current-change="onCurrentChange"
layout="total, sizes, prev, pager, next, jumper"
/>
</div>
</el-card>
<RegionForm ref="formRef" :title="state.formTitle"></RegionForm>
</my-layout>
</template>
<script lang="ts" setup name="admin/region">
import { ref, reactive, onMounted, getCurrentInstance, onBeforeMount, defineAsyncComponent } from 'vue'
import { PageInputRegionGetPageInput, RegionGetPageOutput, RegionLevel } from '/@/api/admin/data-contracts'
import { RegionLevel as RegionLevelEnum } from '/@/api/admin/enum-contracts'
import { RegionApi } from '/@/api/admin/Region'
import eventBus from '/@/utils/mitt'
import { auth } from '/@/utils/authFunction'
import { toOptionsByValue, getDescByValue } from '/@/utils/enum'
import type { FormInstance } from 'element-plus'
//
const RegionForm = defineAsyncComponent(() => import('./components/region-form.vue'))
const RegionSelect = defineAsyncComponent(() => import('./components/region-select.vue'))
const { proxy } = getCurrentInstance() as any
const regionSelectRef = ref()
const filterFormRef = ref<FormInstance>()
const formRef = ref()
const syncRef = ref()
const popoverRef = ref()
const state = reactive({
loading: false,
sync: {
loading: false,
regionLevel: 2 as RegionLevel,
},
formTitle: '',
total: 0,
statusList: [
{ name: '全部', value: undefined },
{ name: '启用', value: true },
{ name: '禁用', value: false },
],
hotList: [
{ name: '全部', value: undefined },
{ name: '是', value: true },
{ name: '否', value: false },
],
regionLevelList: toOptionsByValue(RegionLevelEnum),
filter: {
parentId: undefined as number | undefined,
name: '',
enabled: undefined,
hot: undefined,
level: undefined,
},
pageInput: {
currentPage: 1,
pageSize: 20,
} as PageInputRegionGetPageInput,
dataList: [] as Array<RegionGetPageOutput>,
})
onMounted(async () => {
await onQuery()
eventBus.off('refreshRegion')
eventBus.on('refreshRegion', async () => {
onQuery()
})
})
onBeforeMount(() => {
eventBus.off('refreshRegion')
})
const formatterEnum = (row: any, column: any, cellValue: any) => {
return getDescByValue(RegionLevelEnum, cellValue)
}
const onSizeChange = (val: number) => {
state.pageInput.currentPage = 1
state.pageInput.pageSize = val
onQuery()
}
const onCurrentChange = (val: number) => {
state.pageInput.currentPage = val
onQuery()
}
const onQuery = async () => {
state.loading = true
state.pageInput.filter = state.filter
const res = await new RegionApi().getPage(state.pageInput).catch(() => {
state.loading = false
})
state.dataList = res?.data?.list ?? []
state.total = res?.data?.total ?? 0
state.loading = false
}
const onReset = () => {
regionSelectRef.value.reset()
filterFormRef.value!.resetFields()
onQuery()
}
const onAdd = () => {
state.formTitle = '新增地区'
formRef.value.open()
}
const onEdit = (row: RegionGetPageOutput) => {
state.formTitle = '编辑地区'
formRef.value.open(row)
}
const onDelete = (row: RegionGetPageOutput) => {
proxy.$modal
.confirmDelete(`确定要删除地区【${row.name}】?`)
.then(async () => {
await new RegionApi().delete({ id: row.id }, { loading: true })
onQuery()
})
.catch(() => {})
}
//
const onSetEnable = (row: RegionGetPageOutput & { loading: boolean }) => {
return new Promise((resolve, reject) => {
proxy.$modal
.confirm(`确定要${row.enabled ? '禁用' : '启用'}${row.name}】?`)
.then(async () => {
row.loading = true
const res = await new RegionApi()
.setEnable({ regionId: row.id, enabled: !row.enabled }, { showSuccessMessage: true })
.catch(() => {
reject(new Error('Error'))
})
.finally(() => {
row.loading = false
})
if (res && res.success) {
resolve(true)
onQuery()
} else {
reject(new Error('Cancel'))
}
})
.catch(() => {
reject(new Error('Cancel'))
})
})
}
//
const onSetHot = (row: RegionGetPageOutput & { loading: boolean; hotLoading: boolean }) => {
return new Promise((resolve, reject) => {
proxy.$modal
.confirm(`确定要${row.hot ? '关闭' : '开启'}${row.name}】热门?`)
.then(async () => {
row.hotLoading = true
const res = await new RegionApi()
.setHot({ regionId: row.id, hot: !row.hot }, { showSuccessMessage: true })
.catch(() => {
reject(new Error('Error'))
})
.finally(() => {
row.hotLoading = false
})
if (res && res.success) {
resolve(true)
onQuery()
} else {
reject(new Error('Cancel'))
}
})
.catch(() => {
reject(new Error('Cancel'))
})
})
}
const onSyncCancel = () => {
popoverRef.value?.hide?.()
}
const onSync = async () => {
onSyncCancel()
state.sync.loading = true
await new RegionApi()
.syncData(state.sync.regionLevel, { showErrorMessage: false })
.then(() => {
proxy.$modal.msgSuccess(`同步完成`)
onQuery()
})
.catch(() => {
proxy.$modal.msgError(`同步失败`)
})
.finally(() => {
state.sync.loading = false
})
}
</script>
<style scoped lang="scss"></style>

View File

@ -1,272 +0,0 @@
<template>
<div>
<el-dialog v-model="state.showDialog" destroy-on-close :title="title" draggable :close-on-click-modal="false"
:close-on-press-escape="false" width="600px">
<el-form :model="form" ref="formRef" label-width="110px">
<el-row :gutter="35">
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="图片" prop="imgUrl">
<el-upload class="remote-uploader" :action="remoteUrlAction"
:headers="uploadHeaders" :data="{ autoUpdate: true }" :show-file-list="false"
:before-upload="beforeUpload" :on-success="remoteUrlSuccess" :on-error="remoteUrlError">
<img style="width: 100px; height: 100px;" :src="remoteUrl" />
</el-upload>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="站点" prop="site"
:rules="[{ required: true, message: '请选择站点', trigger: ['blur', 'change'] }]">
<el-select v-model="form.site" placeholder="请选择站点" clearable class="w100">
<el-option v-for="item in state.Site" :key="item.code" :label="item.name"
:value="item.code" />
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="名称" prop="name"
:rules="[{ required: true, message: '请输入名称', trigger: ['blur', 'change'] }]">
<el-input v-model="form.name" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="IP地址" prop="ipAddress"
:rules="[{ required: true, message: '请输入IP地址', trigger: ['blur', 'change'] }]">
<el-input v-model="form.ipAddress" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="域" prop="domain"
:rules="[{ required: true, message: '请输入域', trigger: ['blur', 'change'] }]">
<el-input v-model="form.domain" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="用户名" prop="username">
<el-input v-model="form.username" clearable />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel"> </el-button>
<el-button type="primary" @click="onSure" :loading="state.sureLoading"> </el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup name="admin/remote/form">
import { reactive, toRefs, ref, getCurrentInstance, computed } from 'vue'
import { RemoteUpdateInput, DictGetListOutput } from '/@/api/admin/data-contracts'
import { RemoteApi } from '/@/api/admin/Remote'
import { DictApi } from '/@/api/admin/Dict'
import { cloneDeep } from 'lodash-es'
import eventBus from '/@/utils/mitt'
import { ElMessage } from 'element-plus'
import { useUserInfo } from '/@/stores/userInfo'
import pinia from '/@/stores/index'
import { storeToRefs } from 'pinia'
import { AxiosResponse } from 'axios'
import type { UploadProps, UploadInstance, UploadUserFile } from 'element-plus'
const { proxy } = getCurrentInstance() as any
const storesUserInfo = useUserInfo(pinia)
const { userInfos } = storeToRefs(storesUserInfo)
/** 字典分类 */
/** 字典分类 */
const DictType = {
RemoteSite: { dictTypeCode: 'RemoteSite', desc: '站点' },
}
defineProps({
title: {
type: String,
default: '',
},
})
const formRef = ref()
const state = reactive({
showDialog: false,
sureLoading: false,
Site: [] as DictGetListOutput[] | null,
form: {} as RemoteUpdateInput,
remoteUrlLoading: false,
updateLoading: false,
token: storesUserInfo.getToken(),
})
const { form } = toRefs(state)
//
const getDictList = async () => {
const res = await new DictApi().getList([DictType.RemoteSite.dictTypeCode]).catch(() => { })
if (res?.success && res.data) {
state.Site = res.data.remoteSite
}
}
//
const open = async (row: Partial<RemoteUpdateInput> = { id: 0 }) => {
await getDictList()
let formData = cloneDeep(row) as RemoteUpdateInput
if (row.id && row.id > 0) {
const res = await new RemoteApi().get({ id: row.id })
if (res?.success) {
formData = res.data as RemoteUpdateInput
}
} else {
formData = {
id: 0,
imgUrl: '',
site: '',
name: '',
ipAddress: '',
domain: '',
username: '',
}
}
state.form = formData
state.showDialog = true
}
//
const onCancel = () => {
state.showDialog = false
}
//
const onSure = () => {
formRef.value.validate(async (valid: boolean) => {
if (!valid) return
state.sureLoading = true
let res = {} as any
if (state.form.id != undefined && state.form.id > 0) {
res = await new RemoteApi().update(state.form).catch(() => {
state.sureLoading = false
})
} else {
res = await new RemoteApi().add(state.form).catch(() => {
state.sureLoading = false
})
}
state.sureLoading = false
if (res?.success === true) {
eventBus.emit('refreshRemote')
state.showDialog = false
}
})
}
//
const uploadHeaders = computed(() => {
return {
Authorization: 'Bearer ' + storesUserInfo.getToken()
}
})
//
const remoteUrl = computed(() => {
return state.form.imgUrl || import.meta.env.VITE_API_URL + '/upload/2025-06-24/685abf87-d315-b804-0004-bfbb4a755111.png'
})
// url
const remoteUrlAction = computed(() => {
return import.meta.env.VITE_API_URL + '/api/admin/remote/remote-upload'
})
const beforeUpload: UploadProps['beforeUpload'] = (file) => {
state.token = storesUserInfo.getToken()
console.log("🔍 ~ beforeUpload ~ state.token:", state.token)
state.remoteUrlLoading = true
return true
}
const remoteUrlSuccess = (res: AxiosResponse) => {
state.remoteUrlLoading = false
if (!res?.success) {
if (res.msg) {
proxy.$modal.msgError(res.msg)
}
return
}
state.form.imgUrl = res.data
}
//
const remoteUrlError = (error: any) => {
state.remoteUrlLoading = false
let message = ''
if (error.message) {
try {
message = JSON.parse(error.message)?.msg
} catch (err) {
message = error.message || ''
}
}
if (message) proxy.$modal.msgError(message)
}
defineExpose({
open,
})
</script>
<style scoped lang="scss">
.remote-uploader {
:deep(.el-upload--picture-card) {
--el-upload-picture-card-size: 100px;
width: var(--el-upload-picture-card-size);
height: var(--el-upload-picture-card-size);
line-height: var(--el-upload-picture-card-size);
}
:deep(.el-upload-list--picture-card .el-upload-list__item) {
width: var(--el-upload-picture-card-size);
height: var(--el-upload-picture-card-size);
}
:deep(.el-upload-list__item-thumbnail) {
width: 100%;
height: 100%;
object-fit: cover;
}
:deep(.el-icon) {
font-size: 20px;
color: var(--el-text-color-regular);
}
:deep(.el-upload--picture-card:hover) {
border-color: var(--el-color-primary);
}
:deep(.el-upload-list__item-actions) {
span {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
cursor: pointer;
&:hover {
color: var(--el-color-primary);
}
}
}
}
</style>

View File

@ -1,201 +0,0 @@
<template>
<my-layout>
<div class="my-query-box mt8" style="position: relative">
<el-card shadow="never" :body-style="{ paddingBottom: '0' }">
<el-form :inline="true" label-width="auto" :label-position="'left'" @submit.stop.prevent>
<el-form-item label="关键词">
<el-input v-model="state.filter.name" placeholder="请输入名称、IP地址、域名" style="width: 350px" @keyup.enter="onQuery" />
</el-form-item>
<el-form-item label="站点">
<el-select v-model="state.filter.site" placeholder="请选择站点" clearable style="width: 150px" @change="onQuery">
<el-option v-for="item in state.Site" :key="item.code" :label="item.name" :value="item.code" />
</el-select>
</el-form-item>
<el-form-item label="创建日期">
<MyDateRange v-model:startDate="state.filter.startCreatedTime" v-model:endDate="state.filter.endCreatedTime" style="width: 230px" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="ele-Search" @click="onQuery"> 查询 </el-button>
<el-button v-auth="'api:admin:remote:add'" type="primary" icon="ele-Plus" @click="onAdd"> 新增 </el-button>
</el-form-item>
</el-form>
</el-card>
</div>
<el-card class="my-fill mt8 el-card-table" shadow="never">
<el-table ref="tableRef" v-loading="state.loading" :data="state.remoteListData" row-key="id" style="width: 100%" border>
<el-table-column type="index" label="序号" width="60" :index="indexMethod" />
<el-table-column prop="imgUrl" label="图片" width="100">
<template #default="{ row }">
<el-image :src="row.imgUrl" fit="contain" style="width: 50px; height: 50px" />
</template>
</el-table-column>
<el-table-column prop="name" label="名称" min-width="120" />
<el-table-column prop="site" label="站点" min-width="120" />
<el-table-column prop="ipAddress" label="IP地址" min-width="120" />
<el-table-column prop="domain" label="域" min-width="120" />
<el-table-column prop="username" label="用户名" min-width="120" />
<el-table-column prop="createdTime" label="创建时间" :formatter="formatterTime" width="100" />
<el-table-column label="操作" width="150" fixed="right" header-align="center" align="center">
<template #default="{ row }">
<el-button v-auth="'api:admin:remote:update'" icon="ele-Edit" text type="primary" @click="onUpdate(row)">修改</el-button>
<el-button v-auth="'api:admin:remote:delete'" icon="ele-Delete" text type="danger" @click="onDelete(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="my-flex my-flex-end" style="margin-top: 10px">
<el-pagination
v-model:currentPage="state.pageInput.currentPage"
v-model:page-size="state.pageInput.pageSize"
:total="state.total"
:page-sizes="[10, 20, 50, 100]"
background
@size-change="onSizeChange"
@current-change="onCurrentChange"
layout="total, sizes, prev, pager, next, jumper"
/>
</div>
</el-card>
<remote-form ref="remoteFormRef" :title="state.remoteFormTitle"></remote-form>
</my-layout>
</template>
<script lang="ts" setup name="admin/remote">
import { ref, reactive, onMounted, onBeforeMount, getCurrentInstance, defineAsyncComponent } from 'vue'
import { ElTable, ElMessageBox, ElMessage } from 'element-plus'
import { RemoteGetOutput, PageInputRemoteGetPageInput, DictGetListOutput } from '/@/api/admin/data-contracts'
import { RemoteApi } from '/@/api/admin/Remote'
import { DictApi } from '/@/api/admin/Dict'
import dayjs from 'dayjs'
import eventBus from '/@/utils/mitt'
//
const RemoteForm = defineAsyncComponent(() => import('./components/remote-form.vue'))
const MyDateRange = defineAsyncComponent(() => import('/@/components/my-date-range/index.vue'))
const MyUpload = defineAsyncComponent(() => import('/@/components/my-upload/index.vue'))
const { proxy } = getCurrentInstance() as any
const remoteFormRef = ref()
const tableRef = ref<InstanceType<typeof ElTable>>()
/** 字典分类 */
const DictType = {
RemoteSite: { dictTypeCode: 'RemoteSite', desc: '站点' },
}
const state = reactive({
loading: false,
remoteFormTitle: '',
showDialog: false,
sureLoading: false,
Site: [] as DictGetListOutput[],
filter: {
name: '',
site: '',
startCreatedTime: undefined,
endCreatedTime: undefined,
},
total: 0,
pageInput: {
currentPage: 1,
pageSize: 20,
filter: {},
} as PageInputRemoteGetPageInput,
remoteListData: [] as Array<RemoteGetOutput>,
})
onMounted(() => {
getDictList()
onQuery()
eventBus.off('refreshRemote')
eventBus.on('refreshRemote', async () => {
onQuery()
})
})
onBeforeMount(() => {
eventBus.off('refreshRemote')
})
//
const formatterTime = (row: any, column: any, cellValue: any) => {
if (!cellValue) return ''
return dayjs(cellValue).format('YYYY-MM-DD')
}
//
const indexMethod = (index: number) => {
return ((state.pageInput.currentPage || 1) - 1) * (state.pageInput.pageSize || 20) + index + 1
}
//
const onQuery = async () => {
state.loading = true
const res = await new RemoteApi()
.getPage({
...state.pageInput,
filter: state.filter,
})
.catch(() => {
state.loading = false
})
state.loading = false
if (res?.success) {
state.remoteListData = res.data?.list ?? []
state.total = res.data?.total ?? 0
}
}
//
const onAdd = () => {
state.remoteFormTitle = '新增'
remoteFormRef.value.open()
}
//
const onUpdate = (row: RemoteGetOutput) => {
state.remoteFormTitle = '修改'
remoteFormRef.value.open(row)
}
//
const onDelete = async (row: RemoteGetOutput) => {
ElMessageBox.confirm(`确定删除远程连接:【${row.name}】?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(async () => {
const res = await new RemoteApi().delete({ id: row.id })
if (res?.status === 200) {
ElMessage.success('删除成功')
onQuery()
}
})
.catch(() => {})
}
//
const onSizeChange = (val: number) => {
state.pageInput.pageSize = val
onQuery()
}
//
const onCurrentChange = (val: number) => {
state.pageInput.currentPage = val
onQuery()
}
//
const getDictList = async () => {
const res = await new DictApi().getList([DictType.RemoteSite.dictTypeCode]).catch(() => {})
if (res?.success && res.data) {
state.Site = res.data.remoteSite || []
}
}
</script>

View File

@ -1,142 +0,0 @@
<template>
<div>
<el-dialog
v-model="state.showDialog"
destroy-on-close
:title="title"
draggable
:close-on-click-modal="false"
:close-on-press-escape="false"
width="600px"
>
<el-form :model="form" ref="formRef" label-width="80px">
<el-row :gutter="35">
<el-col v-if="form.type === 2" :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="上级分组" prop="parentId" :rules="[{ required: true, message: '请选择上级分组', trigger: ['change'] }]">
<el-tree-select
v-model="form.parentId"
:data="roleTreeData"
node-key="id"
:props="{ label: 'name' }"
check-strictly
default-expand-all
render-after-expand
fit-input-width
clearable
class="w100"
/>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="名称" prop="name" :rules="[{ required: true, message: '请输入名称', trigger: ['blur', 'change'] }]">
<el-input v-model="form.name" clearable />
</el-form-item>
</el-col>
<el-col v-if="form.type === 2" :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="编码" prop="code">
<el-input v-model="form.code" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="排序">
<el-input-number v-model="form.sort" />
</el-form-item>
</el-col>
<el-col v-if="form.type === 2" :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="说明">
<el-input v-model="form.description" clearable type="textarea" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel"> </el-button>
<el-button type="primary" @click="onSure" :loading="state.sureLoading"> </el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup name="admin/role/form">
import { reactive, toRefs, ref, PropType } from 'vue'
import { RoleGetListOutput, RoleUpdateInput } from '/@/api/admin/data-contracts'
import { RoleApi } from '/@/api/admin/Role'
import { cloneDeep } from 'lodash-es'
import eventBus from '/@/utils/mitt'
defineProps({
title: {
type: String,
default: '',
},
roleTreeData: {
type: Array as PropType<RoleGetListOutput[]>,
default: () => [],
},
})
const formRef = ref()
const state = reactive({
showDialog: false,
sureLoading: false,
form: {} as RoleUpdateInput,
})
const { form } = toRefs(state)
//
const open = async (row: RoleUpdateInput = { id: 0 }) => {
let formData = cloneDeep(row) as RoleUpdateInput
if (row.id > 0) {
const res = await new RoleApi().get({ id: row.id }, { loading: true })
if (res?.success) {
formData = res.data as RoleUpdateInput
formData.parentId = formData.parentId && formData.parentId > 0 ? formData.parentId : undefined
} else {
return
}
}
state.form = formData
state.showDialog = true
}
//
const onCancel = () => {
state.showDialog = false
}
//
const onSure = () => {
formRef.value.validate(async (valid: boolean) => {
if (!valid) return
state.sureLoading = true
let res = {} as any
state.form.parentId = state.form.parentId && state.form.parentId > 0 ? state.form.parentId : undefined
if (state.form.id != undefined && state.form.id > 0) {
res = await new RoleApi().update(state.form, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
} else {
res = await new RoleApi().add(state.form, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
}
state.sureLoading = false
if (res?.success) {
eventBus.emit('refreshRole')
state.showDialog = false
}
})
}
defineExpose({
open,
})
</script>

View File

@ -1,131 +0,0 @@
<template>
<el-dialog
v-model="state.showDialog"
destroy-on-close
:title="innerTitle"
append-to-body
draggable
:close-on-click-modal="false"
:close-on-press-escape="false"
width="480px"
>
<el-form :model="form" ref="formRef" label-width="80px" label-position="top">
<el-row :gutter="35">
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="数据范围">
<el-select key="dataScope" v-model="form.dataScope" placeholder="请选择" class="w100">
<el-option v-for="item in state.dataScopeList" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<org-menu
ref="orgRef"
show-checkbox
check-on-click-node
v-model="form.orgIds"
:default-checked-keys="form.orgIds"
class="w100"
v-show="form.dataScope === 5"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel"> </el-button>
<el-button type="primary" @click="onSure" :loading="state.sureLoading"> </el-button>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup name="admin/role/components/set-role-data-scope">
import { ref, toRefs, reactive, computed, defineAsyncComponent } from 'vue'
import { RoleGetListOutput, RoleSetDataScopeInput } from '/@/api/admin/data-contracts'
import { RoleApi } from '/@/api/admin/Role'
const OrgMenu = defineAsyncComponent(() => import('/@/views/admin/org/components/org-menu.vue'))
const orgRef = ref()
const props = defineProps({
title: {
type: String,
default: '',
},
})
const innerTitle = computed(() => {
return props.title ? props.title : state.roleName ? `设置【${state.roleName}】数据权限` : '设置数据权限'
})
const state = reactive({
showDialog: false,
loading: false,
sureLoading: false,
permissionTreeData: [],
roleId: 0 as number | undefined,
roleName: '' as string | undefined | null,
checkedKeys: [] as number[] | undefined | null,
form: {} as RoleSetDataScopeInput,
dataScopeList: [
{ label: '全部', value: 1 },
{ label: '本部门和下级部门', value: 2 },
{ label: '本部门', value: 3 },
{ label: '本人数据', value: 4 },
{ label: '指定部门', value: 5 },
],
})
const { form } = toRefs(state)
//
const open = async (role: RoleGetListOutput) => {
state.roleId = role.id
state.roleName = role.name
if ((role.id as number) > 0) {
const res = await new RoleApi().get({ id: role.id }, { loading: true })
if (res?.success) {
const data = res.data
state.form = { roleId: data?.id, dataScope: data?.dataScope, orgIds: data?.orgIds } as RoleSetDataScopeInput
state.showDialog = true
}
}
}
//
const close = () => {
state.showDialog = false
}
//
const onCancel = () => {
state.showDialog = false
}
//
const onSure = async () => {
state.sureLoading = true
const res = await new RoleApi().setDataScope(state.form, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
state.sureLoading = false
if (res?.success) {
state.showDialog = false
}
}
defineExpose({
open,
close,
})
</script>
<style scoped lang="scss">
:deep(.el-dialog__body) {
padding: 5px 10px;
}
</style>

View File

@ -1,215 +0,0 @@
<template>
<el-dialog
v-model="state.showDialog"
destroy-on-close
append-to-body
draggable
:close-on-click-modal="false"
:close-on-press-escape="false"
width="780px"
>
<template #header="{ close, titleId, titleClass }">
<div class="my-header">
<div :id="titleId" :class="titleClass">
设置{{ innerTitle }}
<el-select v-model="state.platform" placeholder="请选择所属平台" style="width: 100px" @change="onQuery">
<el-option v-for="item in state.dictData[DictType.PlatForm.name]" :key="item.code" :label="item.name" :value="item.code" />
</el-select>
菜单权限
</div>
</div>
</template>
<div>
<el-tree
ref="permissionTreeRef"
:data="state.permissionTreeData"
node-key="id"
show-checkbox
highlight-current
default-expand-all
check-on-click-node
:expand-on-click-node="false"
:props="{ class: customNodeClass }"
:default-checked-keys="state.checkedKeys"
/>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel"> </el-button>
<el-button type="primary" @click="onSure" :loading="state.sureLoading"> </el-button>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup name="admin/role/components/set-role-menu">
import { ref, reactive, getCurrentInstance, computed, markRaw } from 'vue'
import { RoleGetListOutput, PermissionAssignInput, DictGetListOutput, PermissionGetPermissionListOutput } from '/@/api/admin/data-contracts'
import { PermissionApi } from '/@/api/admin/Permission'
import { ElTree } from 'element-plus'
import { DictApi } from '/@/api/admin/Dict'
import { PlatformType } from '/@/api/admin.extend/enum-contracts'
/** 字典分类 */
const DictType = {
PlatForm: { name: 'platform', desc: '平台' },
}
const props = defineProps({
title: {
type: String,
default: '',
},
})
const innerTitle = computed(() => {
return props.title ? props.title : state.roleName ? `${state.roleName}` : ''
})
const state = reactive({
showDialog: false,
loading: false,
sureLoading: false,
permissionTreeData: [] as PermissionGetPermissionListOutput[],
roleId: 0 as number | undefined,
roleName: '' as string | undefined | null,
checkedKeys: [],
platform: PlatformType.Web.name,
dictData: {
[DictType.PlatForm.name]: [] as DictGetListOutput[] | null,
},
})
const { proxy } = getCurrentInstance() as any
const permissionTreeRef = ref<InstanceType<typeof ElTree>>()
const getDictList = async () => {
const res = await new DictApi().getList([DictType.PlatForm.name]).catch(() => {})
if (res?.success && res.data) {
state.dictData = markRaw(res.data)
}
}
const getRolePermissionList = async () => {
const res = await new PermissionApi().getRolePermissionList({ roleId: state.roleId })
state.checkedKeys = res?.success ? (res.data as never[]) : []
}
//
const open = async (role: RoleGetListOutput) => {
await getDictList()
state.roleId = role.id
state.roleName = role.name
proxy.$modal.loading()
await onQuery()
await getRolePermissionList()
proxy.$modal.closeLoading()
state.showDialog = true
}
//
const close = () => {
state.showDialog = false
}
const onQuery = async () => {
state.loading = true
const res = await new PermissionApi().getPermissionList({ platform: state.platform }).catch(() => {
state.loading = false
})
if (res && res.data && res.data.length > 0) {
state.permissionTreeData = res.data
} else {
state.permissionTreeData = []
}
state.loading = false
}
const customNodeClass = (data: any, node: any) => {
let isPenultimate = true
for (const key in data.children) {
if (data.children[key]?.children?.length ?? 0 > 0) {
isPenultimate = false
break
}
}
return data.children?.length > 0 && isPenultimate ? `is-penultimate level${node.level}` : ''
}
//
const onCancel = () => {
state.showDialog = false
}
//
const onSure = async () => {
state.sureLoading = true
const permissionIds = permissionTreeRef.value?.getCheckedKeys(true)
const input = {
platform: state.platform,
roleId: state.roleId,
permissionIds: permissionIds,
} as PermissionAssignInput
const res = await new PermissionApi().assign(input, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
state.sureLoading = false
if (res?.success) {
state.showDialog = false
}
}
defineExpose({
open,
close,
})
</script>
<style scoped lang="scss">
:deep(.el-dialog__body) {
padding: 5px 10px;
}
:deep(.is-penultimate) {
.el-tree-node__children {
padding-left: 65px;
white-space: pre-wrap;
line-height: 100%;
.el-tree-node {
display: inline-block;
}
.el-tree-node__content {
padding-left: 12px !important;
padding-right: 12px;
.el-tree-node__expand-icon.is-leaf {
display: none;
}
}
}
&.level1 {
.el-tree-node__children {
padding-left: 36px;
}
}
&.level2 {
.el-tree-node__children {
padding-left: 54x;
}
}
&.level3 {
.el-tree-node__children {
padding-left: 72px;
}
}
&.level4 {
.el-tree-node__children {
padding-left: 90px;
}
}
}
</style>

View File

@ -1,333 +0,0 @@
<template>
<MySplitPanes>
<pane size="50" min-size="30" max-size="70">
<div class="my-flex-column w100 h100">
<el-card class="my-query-box mt8" shadow="never">
<el-form :inline="true" @submit.stop.prevent>
<el-form-item label="角色名称">
<el-input v-model="state.filter.roleName" placeholder="角色名称" @keyup.enter="onQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="ele-Search" @click="onQuery"> 查询 </el-button>
<el-dropdown v-auth="'api:admin:role:add'" style="margin-left: 12px">
<el-button type="primary"
>新增<el-icon class="el-icon--right"><ele-ArrowDown /></el-icon
></el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="onAdd(1)">新增分组</el-dropdown-item>
<el-dropdown-item @click="onAdd(2)">新增角色</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</el-form-item>
</el-form>
</el-card>
<el-card class="my-fill mt8" shadow="never">
<el-table
ref="roleTableRef"
v-loading="state.loading"
:data="state.roleTreeData"
row-key="id"
default-expand-all
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
highlight-current-row
style="width: 100%"
border
@current-change="onCurrentChange"
>
<el-table-column prop="name" label="角色名称" min-width="120" show-overflow-tooltip />
<el-table-column prop="sort" label="排序" width="82" align="center" show-overflow-tooltip />
<el-table-column label="操作" width="100" fixed="right" header-align="center" align="right">
<template #default="{ row }">
<el-button v-if="row.type === 1" v-auth="'api:admin:role:add'" icon="ele-Plus" text type="primary" @click="onAdd(2, row)"></el-button>
<my-dropdown-more icon-only v-auths="['api:admin:permission:assign', 'api:admin:role:update', 'api:admin:role:delete']">
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item v-if="row.type === 2 && auth('api:admin:permission:assign')" @click="onSetRoleMenu(row)"
>菜单权限</el-dropdown-item
>
<el-dropdown-item v-if="row.type === 2" @click="onSetRoleDataScope(row)">数据权限</el-dropdown-item>
<el-dropdown-item v-if="auth('api:admin:role:update')" @click="onEdit(row)"
>编辑{{ row.type === 1 ? '分组' : '角色' }}</el-dropdown-item
>
<el-dropdown-item v-if="auth('api:admin:role:delete')" @click="onDelete(row)"
>删除{{ row.type === 1 ? '分组' : '角色' }}</el-dropdown-item
>
</el-dropdown-menu>
</template>
</my-dropdown-more>
</template>
</el-table-column>
</el-table>
</el-card>
</div>
</pane>
<pane>
<div class="my-flex-column w100 h100">
<el-card class="my-query-box mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
<el-form :inline="true" @submit.stop.prevent>
<el-form-item label="姓名">
<el-input v-model="state.filter.name" placeholder="姓名" @keyup.enter="onGetRoleUserList" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="ele-Search" @click="onGetRoleUserList"> 查询 </el-button>
<el-button v-auth="'api:admin:role:add-role-user'" type="primary" icon="ele-Plus" @click="onAddUser"> 添加员工 </el-button>
<el-button v-auth="'api:admin:role:remove-role-user'" type="danger" icon="ele-Delete" @click="onRemoveUser"> 移除员工 </el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="my-fill mt8" shadow="never">
<el-table
ref="userTableRef"
v-loading="state.userListLoading"
:data="state.userListData"
row-key="id"
style="width: 100%"
border
@row-click="onUserRowClick"
>
<el-table-column type="selection" width="55" />
<el-table-column prop="name" label="姓名" min-width="120" show-overflow-tooltip />
<el-table-column prop="mobile" label="手机号" min-width="120" show-overflow-tooltip />
<el-table-column prop="email" label="邮箱" min-width="180" show-overflow-tooltip />
</el-table>
</el-card>
</div>
</pane>
<role-form ref="roleFormRef" :title="state.roleFormTitle" :role-tree-data="state.roleFormTreeData"></role-form>
<user-select
ref="userSelectRef"
:title="`添加【${state.roleName}】员工`"
multiple
:sure-loading="state.sureLoading"
@sure="onSureUser"
></user-select>
<set-role-menu ref="setRoleMenuRef"></set-role-menu>
<set-role-data-scope ref="setRoleDataScopeRef"></set-role-data-scope>
</MySplitPanes>
</template>
<script lang="ts" setup name="admin/role">
import { ref, reactive, onMounted, getCurrentInstance, onBeforeMount, nextTick, defineAsyncComponent } from 'vue'
import { RoleGetListOutput, RoleGetRoleUserListOutput, UserGetPageOutput, RoleAddRoleUserListInput, RoleType } from '/@/api/admin/data-contracts'
import { RoleApi } from '/@/api/admin/Role'
import { listToTree, filterTree } from '/@/utils/tree'
import { ElTable } from 'element-plus'
import { cloneDeep } from 'lodash-es'
import eventBus from '/@/utils/mitt'
import { auth } from '/@/utils/authFunction'
import { Pane } from 'splitpanes'
//
const RoleForm = defineAsyncComponent(() => import('./components/role-form.vue'))
const SetRoleMenu = defineAsyncComponent(() => import('./components/set-role-menu.vue'))
const SetRoleDataScope = defineAsyncComponent(() => import('./components/set-role-data-scope.vue'))
const UserSelect = defineAsyncComponent(() => import('/@/views/admin/user/components/user-select.vue'))
const MyDropdownMore = defineAsyncComponent(() => import('/@/components/my-dropdown-more/index.vue'))
const MySplitPanes = defineAsyncComponent(() => import('/@/components/my-layout/split-panes.vue'))
const { proxy } = getCurrentInstance() as any
const roleTableRef = ref()
const roleFormRef = ref()
const userTableRef = ref<InstanceType<typeof ElTable>>()
const userSelectRef = ref()
const setRoleMenuRef = ref()
const setRoleDataScopeRef = ref()
const state = reactive({
loading: false,
userListLoading: false,
sureLoading: false,
roleFormTitle: '',
filter: {
name: '',
roleName: '',
},
roleTreeData: [] as any,
roleFormTreeData: [] as any,
userListData: [] as RoleGetRoleUserListOutput[],
roleId: undefined as number | undefined,
roleName: '' as string | null | undefined,
})
onMounted(() => {
onQuery()
eventBus.off('refreshRole')
eventBus.on('refreshRole', async () => {
onQuery()
})
})
onBeforeMount(() => {
eventBus.off('refreshRole')
})
const onQuery = async () => {
state.loading = true
const res = await new RoleApi().getList().catch(() => {
state.loading = false
})
if (res && res.data && res.data.length > 0) {
state.roleTreeData = filterTree(listToTree(cloneDeep(res.data)), state.filter.roleName)
state.roleFormTreeData = listToTree(cloneDeep(res.data).filter((a) => a.parentId === 0))
if (state.roleTreeData.length > 0 && state.roleTreeData[0].children?.length > 0) {
nextTick(() => {
roleTableRef.value!.setCurrentRow(state.roleTreeData[0].children[0])
})
}
} else {
state.roleTreeData = []
state.roleFormTreeData = []
}
state.loading = false
}
const onAdd = (type: RoleType, row: RoleGetListOutput | undefined = undefined) => {
switch (type) {
case 1:
state.roleFormTitle = '新增分组'
roleFormRef.value.open({ type: 1 })
break
case 2:
state.roleFormTitle = '新增角色'
roleFormRef.value.open({ type: 2, parentId: row?.id, dataScope: 1 })
break
}
}
const onEdit = (row: RoleGetListOutput) => {
switch (row.type) {
case 1:
state.roleFormTitle = '编辑分组'
break
case 2:
state.roleFormTitle = '编辑角色'
break
}
roleFormRef.value.open(row)
}
const onDelete = (row: RoleGetListOutput) => {
proxy.$modal
.confirmDelete(`确定要删除角色【${row.name}】?`)
.then(async () => {
await new RoleApi().delete({ id: row.id }, { loading: true })
onQuery()
})
.catch(() => {})
}
const onGetRoleUserList = async () => {
state.userListLoading = true
const res = await new RoleApi().getRoleUserList({ RoleId: state.roleId, Name: state.filter.name }).catch(() => {
state.userListLoading = false
})
state.userListLoading = false
if (res?.success) {
if (res.data && res.data.length > 0) {
state.userListData = res.data
} else {
state.userListData = []
}
}
}
const onCurrentChange = (currentRow: RoleGetListOutput, oldCurrentRow: RoleGetListOutput) => {
if (!currentRow) {
return
}
if (currentRow?.id !== oldCurrentRow?.id && (oldCurrentRow?.id as number) > 0) {
if ((currentRow?.parentId as number) === 0) {
roleTableRef.value!.setCurrentRow(oldCurrentRow)
}
} else {
if ((currentRow?.parentId as number) === 0) {
roleTableRef.value!.setCurrentRow()
}
}
if ((currentRow?.parentId as number) !== 0 && (oldCurrentRow?.parentId as number) !== 0 && (currentRow?.id as number) > 0) {
state.roleId = currentRow.id
state.roleName = currentRow.name
onGetRoleUserList()
}
}
const onUserRowClick = (row: RoleGetRoleUserListOutput) => {
userTableRef.value!.toggleRowSelection(row, undefined)
}
const onAddUser = () => {
if (!((state.roleId as number) > 0)) {
proxy.$modal.msgWarning('请选择角色')
return
}
userSelectRef.value.open({ roleId: state.roleId })
}
const onRemoveUser = () => {
if (!((state.roleId as number) > 0)) {
proxy.$modal.msgWarning('请选择角色')
return
}
const selectionRows = userTableRef.value!.getSelectionRows() as UserGetPageOutput[]
if (!((selectionRows.length as number) > 0)) {
proxy.$modal.msgWarning('请选择员工')
return
}
proxy.$modal
.confirm(`确定要移除吗?`)
.then(async () => {
const userIds = selectionRows?.map((a) => a.id)
const input = { roleId: state.roleId, userIds } as RoleAddRoleUserListInput
await new RoleApi().removeRoleUser(input, { loading: true })
onGetRoleUserList()
})
.catch(() => {})
}
const onSureUser = async (users: UserGetPageOutput[]) => {
if (!(users?.length > 0)) {
userSelectRef.value.close()
return
}
state.sureLoading = true
const userIds = users?.map((a) => a.id)
const input = { roleId: state.roleId, userIds } as RoleAddRoleUserListInput
await new RoleApi().addRoleUser(input, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
state.sureLoading = false
userSelectRef.value.close()
onGetRoleUserList()
}
const onSetRoleMenu = (role: RoleGetListOutput) => {
if (!((role?.id as number) > 0)) {
proxy.$modal.msgWarning('请选择角色')
return
}
setRoleMenuRef.value.open(role)
}
const onSetRoleDataScope = (role: RoleGetListOutput) => {
if (!((role?.id as number) > 0)) {
proxy.$modal.msgWarning('请选择角色')
return
}
setRoleDataScopeRef.value.open(role)
}
</script>
<style scoped lang="scss"></style>

View File

@ -1,73 +0,0 @@
<template>
<div class="w100 h100 my-msg-card-box">
<div class="h100 my-msg-box">
<div class="h100 w-e-text-container my-msg-content-box" v-html="state.msg.content"></div>
</div>
</div>
</template>
<script lang="ts" setup name="admin/site-msg/detail">
import { reactive, onMounted } from 'vue'
import { SiteMsgApi } from '/@/api/admin/SiteMsg'
import { SiteMsgGetContentOutput } from '/@/api/admin/data-contracts'
import { LocationQuery, useRoute } from 'vue-router'
import eventBus from '/@/utils/mitt'
const route = useRoute()
const state = reactive({
loading: false,
query: {} as LocationQuery,
msg: {} as SiteMsgGetContentOutput,
})
onMounted(async () => {
state.query = route.query
await getContent()
if (!state.msg?.isRead) await sedRead()
})
const sedRead = async () => {
const res = await new SiteMsgApi().setRead({ id: (state.query.id || 0) as number }).catch(() => {})
if (res?.success) {
eventBus.emit('refreshSiteMsg')
eventBus.emit('checkUnreadMsg')
}
}
const getContent = async () => {
state.loading = true
const res = await new SiteMsgApi().getContent({ id: Number(state.query.id) }).catch(() => {
state.loading = false
})
state.msg = res?.data as SiteMsgGetContentOutput
state.loading = false
}
</script>
<style scoped lang="scss">
.my-msg-card-box {
padding: 10px;
:deep() {
.el-card__body {
height: 100%;
}
}
.my-msg-box {
padding: 20px;
.my-msg-content-box {
min-width: 320px;
max-width: 700px;
border: 1px solid var(--next-border-color-light);
background-color: var(--el-color-white);
color: var(--el-color-black);
margin: auto;
padding: 30px;
}
}
}
</style>

View File

@ -1,272 +0,0 @@
<template>
<MyLayout>
<el-card v-show="state.showQuery" class="my-query-box mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
<el-form class="my-form-inline" :inline="true" label-width="auto" @submit.stop.prevent>
<el-form-item label="">
<el-select v-model="state.filter.isRead" :empty-values="[undefined]" style="width: 90px" @change="onQuery">
<el-option v-for="status in state.statusList" :key="status.name" :label="status.name" :value="status.value" />
</el-select>
</el-form-item>
<el-form-item label="分类">
<el-tree-select
v-model="state.filter.typeId"
placeholder="请选择分类"
:data="state.msgTypeTreeData"
node-key="id"
:props="{ label: 'name' }"
check-strictly
default-expand-all
fit-input-width
clearable
filterable
@change="onQuery"
/>
</el-form-item>
<el-form-item label="标题">
<el-input v-model="state.filter.title" placeholder="标题" @keyup.enter="onQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="ele-Search" @click="onQuery"> 查询 </el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="my-fill mt8" shadow="never">
<div class="my-tools-box mb8 my-flex my-flex-between">
<div>
<el-button type="danger" :disabled="!isRowSelect" :loading="state.loadingBatchDelete" @click="onBatchDelete">删除</el-button>
<el-button type="primary" :disabled="!isRowSelect" :loading="state.loadingBatchSetRead" @click="onBatchSetRead">标为已读</el-button>
<el-button type="primary" :loading="state.loadingSetAllRead" @click="onSetAllRead">全部已读</el-button>
</div>
<div>
<el-tooltip effect="dark" :content="state.showQuery ? '隐藏查询' : '显示查询'" placement="top">
<el-button :icon="state.showQuery ? 'ele-ArrowUp' : 'ele-ArrowDown'" circle @click="state.showQuery = !state.showQuery" />
</el-tooltip>
</div>
</div>
<el-table
ref="tableRef"
:data="state.msgList"
style="width: 100%"
v-loading="state.loading"
row-key="id"
default-expand-all
border
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
>
<el-table-column type="selection" width="55" />
<el-table-column prop="title" label="标题" min-width="120" show-overflow-tooltip>
<template #default="{ row }">
<div class="my-flex my-flex-items-center">
<MyLink
:model-value="{
path: '/site-msg/detail',
query: { id: row.id, tagsViewName: row.title },
}"
icon="ele-Message"
:type="row.isRead ? '' : 'primary'"
:bold="!row.isRead"
>
{{ row.title }}
</MyLink>
</div>
</template>
</el-table-column>
<el-table-column prop="typeName" label="消息分类" min-width="120" show-overflow-tooltip />
<el-table-column prop="receivedTime" label="接收时间" :formatter="formatterTime" min-width="160" show-overflow-tooltip />
</el-table>
<div class="my-flex my-flex-end" style="margin-top: 10px">
<el-pagination
v-model:currentPage="state.pageInput.currentPage"
v-model:page-size="state.pageInput.pageSize"
:total="state.total"
:page-sizes="[10, 20, 50, 100]"
background
@size-change="onSizeChange"
@current-change="onCurrentChange"
layout="total, sizes, prev, pager, next, jumper"
/>
</div>
</el-card>
</MyLayout>
</template>
<script lang="ts" setup name="admin/site-msg">
import { ref, reactive, onMounted, getCurrentInstance, onBeforeMount, computed, defineAsyncComponent } from 'vue'
import eventBus from '/@/utils/mitt'
import dayjs from 'dayjs'
import { SiteMsgApi } from '/@/api/admin/SiteMsg'
import { PageInputSiteMsgGetPageInput, SiteMsgGetPageOutput, MsgTypeGetListOutput } from '/@/api/admin/data-contracts'
import { listToTree } from '/@/utils/tree'
import { MsgTypeApi } from '/@/api/admin/MsgType'
import { ElTable } from 'element-plus'
const MyLink = defineAsyncComponent(() => import('/@/components/my-link/index.vue'))
const tableRef = ref<InstanceType<typeof ElTable>>()
const { proxy } = getCurrentInstance() as any
const state = reactive({
loading: false,
showQuery: true,
loadingSetAllRead: false,
loadingBatchDelete: false,
loadingBatchSetRead: false,
orgFormTitle: '',
statusList: [
{ name: '全部', value: null },
{ name: '已读', value: true },
{ name: '未读', value: false },
],
filter: {
isRead: null,
typeId: null,
title: '',
},
pageInput: {
currentPage: 1,
pageSize: 20,
filter: {
isRead: null,
typeId: null,
title: '',
},
} as PageInputSiteMsgGetPageInput,
total: 0,
msgList: [] as SiteMsgGetPageOutput[],
msgTypeTreeData: [] as MsgTypeGetListOutput[],
})
const selectionRows = computed(() => {
return tableRef.value?.getSelectionRows()
})
const rowSelectCount = computed(() => {
return selectionRows.value?.length
})
const isRowSelect = computed(() => {
return rowSelectCount.value > 0
})
const selectionIds = computed(() => {
return selectionRows.value?.map((a: any) => a.id)
})
onMounted(async () => {
await getMsgTypes()
onQuery()
eventBus.off('refreshSiteMsg')
eventBus.on('refreshSiteMsg', () => {
onQuery()
})
})
onBeforeMount(() => {
eventBus.off('refreshSiteMsg')
})
const formatterTime = (row: any, column: any, cellValue: any) => {
return cellValue ? dayjs(cellValue).format('YYYY-MM-DD HH:mm:ss') : ''
}
const getMsgTypes = async () => {
const res = await new MsgTypeApi().getList().catch(() => {
state.msgTypeTreeData = []
})
if (res?.success && res.data && res.data.length > 0) {
state.msgTypeTreeData = listToTree(res.data)
} else {
state.msgTypeTreeData = []
}
}
const onQuery = async () => {
state.loading = true
if (state.pageInput.filter) {
state.pageInput.filter = state.filter
}
const res = await new SiteMsgApi().getPage(state.pageInput).catch(() => {
state.loading = false
})
state.msgList = res?.data?.list ?? []
state.total = res?.data?.total ?? 0
state.loading = false
}
const onSizeChange = (val: number) => {
state.pageInput.currentPage = 1
state.pageInput.pageSize = val
onQuery()
}
const onCurrentChange = (val: number) => {
state.pageInput.currentPage = val
onQuery()
}
const onSetAllRead = () => {
proxy.$modal
.confirm(`确认标记所有消息为已读吗?`)
.then(async () => {
state.loadingSetAllRead = true
const res = await new SiteMsgApi().setAllRead().catch(() => {
state.loadingSetAllRead = false
})
state.loadingSetAllRead = false
if (res?.success) {
proxy.$modal.msgSuccess('标记所有已读成功')
onQuery()
}
})
.catch(() => {})
}
const onBatchDelete = () => {
proxy.$modal
.confirmDelete(`确定要删除消息?`)
.then(async () => {
state.loadingBatchDelete = true
const res = await new SiteMsgApi().batchSoftDelete(selectionIds.value).catch(() => {
state.loadingBatchDelete = false
})
state.loadingBatchDelete = false
if (res?.success) {
proxy.$modal.msgSuccess('删除成功')
onQuery()
}
})
.catch(() => {})
}
const onBatchSetRead = () => {
proxy.$modal
.confirmDelete(`确定要标记消息为已读?`)
.then(async () => {
state.loadingBatchSetRead = true
const res = await new SiteMsgApi().batchSetRead(selectionIds.value).catch(() => {
state.loadingBatchSetRead = false
})
state.loadingBatchSetRead = false
if (res?.success) {
proxy.$modal.msgSuccess('标记已读成功')
onQuery()
}
})
.catch(() => {})
}
</script>
<style scoped lang="scss">
.my-form-inline {
:deep() {
.el-select {
--el-select-width: 192px;
}
}
}
</style>

View File

@ -1,112 +0,0 @@
<template>
<el-drawer v-model="state.showDialog" direction="rtl" destroy-on-close :size="size">
<template #header="{ titleId, titleClass }">
<h4 :id="titleId" :class="titleClass">{{ title }}</h4>
<el-icon v-if="state.isFull" class="el-drawer__btn" @click="state.isFull = !state.isFull" title="还原"><ele-CopyDocument /></el-icon>
<el-icon v-else class="el-drawer__btn" @click="state.isFull = !state.isFull" title="最大化"><ele-FullScreen /></el-icon>
</template>
<div class="my-fill h100" style="padding: 20px">
<div class="mb10 my-flex my-flex-end">
<el-button type="primary" @click="onJsonShell">Shell</el-button>
<el-button type="primary" @click="onJsonHttp">Http</el-button>
</div>
<MyJsonEditor ref="jsonEditorRef" v-model="state.content" :options="{ modes: [] }"></MyJsonEditor>
</div>
<template #footer>
<div style="flex: auto; padding: 20px !important">
<el-button @click="onCancel"> </el-button>
<el-button type="primary" @click="onSure"> </el-button>
</div>
</template>
</el-drawer>
</template>
<script lang="ts" setup>
import { reactive, computed, ref } from 'vue'
import MyJsonEditor from '/@/components/my-json-editor/index.vue'
defineProps({
title: {
type: String,
default: 'Json编辑器',
},
})
const emits = defineEmits(['sure'])
const jsonEditorRef = ref()
const state = reactive({
showDialog: false,
isFull: false,
isMobile: document.body.clientWidth < 1000,
content: '',
topic: '',
})
const size = computed(() => {
return state.isMobile ? '100%' : state.isFull ? '100%' : '50%'
})
const onJsonShell = () => {
state.topic = '[shell]'
state.content = `{
"desc": "任务描述",
"arguments": "-plaintext -d \\"{ \\\\\\"id\\\\\\": 1 }\\" \${grpcAddress} YourNamespace.YourGrpcService/YourMethod",
"moduleName": "YourModuleName"
}`
jsonEditorRef.value.jsonEditor.set(JSON.parse(state.content))
}
const onJsonHttp = () => {
state.topic = '[系统预留]Http请求'
state.content = `{
"method": "get",
"url": "",
"header": {
"Content-Type": "application/json"
},
"body": "{}"
}`
jsonEditorRef.value.jsonEditor.set(JSON.parse(state.content))
}
//
const open = (task: any) => {
if (task) {
state.topic = task.topic || ''
state.content = task.body || ''
}
state.showDialog = true
}
//
const onCancel = () => {
state.showDialog = false
}
//
const onSure = () => {
emits('sure', { topic: state.topic, body: state.content })
state.showDialog = false
}
defineExpose({
open,
})
</script>
<style scoped lang="scss">
.el-alert {
border-width: 0px !important;
margin-left: 110px;
margin-top: 10px;
}
.el-drawer__btn {
cursor: pointer;
margin-right: 8px;
&:hover {
color: var(--el-color-primary);
}
}
</style>

View File

@ -1,264 +0,0 @@
<template>
<div>
<el-dialog
v-model="state.showDialog"
destroy-on-close
:title="title"
draggable
:close-on-click-modal="false"
:close-on-press-escape="false"
width="600px"
>
<el-form :model="form" ref="formRef" label-width="110px">
<el-row :gutter="35">
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="任务标题" prop="topic" :rules="[{ required: true, message: '请输入任务标题', trigger: ['blur', 'change'] }]">
<el-input v-model="form.topic" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="任务参数" prop="body">
<template #label>
<div class="my-flex-y-center">
任务参数<el-tooltip effect="dark" placement="top" hide-after="0">
<template #content>设置Json数据</template>
<SvgIcon name="ele-InfoFilled" class="ml5" />
</el-tooltip>
</div>
</template>
<el-input v-model="form.body" clearable type="textarea" rows="6" />
<el-link icon="ele-Edit" underline="never" style="line-height: normal; margin-top: 5px" @click="onOpenJson">Json</el-link>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item prop="alarmEmail">
<template #label>
<div class="my-flex-y-center">
报警邮件<el-tooltip effect="dark" placement="top" hide-after="0">
<template #content>多个邮件地址用逗号分隔</template>
<SvgIcon name="ele-InfoFilled" class="ml5" />
</el-tooltip>
</div>
</template>
<el-input v-model="form.alarmEmail" clearable placeholder="多个邮件地址用逗号分隔" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
<el-form-item prop="failRetryCount">
<template #label>
<div class="my-flex-y-center">失败重试次数</div>
</template>
<el-input-number v-model="form.failRetryCount" :min="0" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
<el-form-item prop="failRetryCount">
<template #label>
<div class="my-flex-y-center">重试间隔</div>
</template>
<el-input-number v-model="form.failRetryInterval" :min="0" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
<el-form-item prop="round" :rules="[{ required: true, message: '请输入执行轮数', trigger: ['blur', 'change'] }]">
<template #label>
<div class="my-flex-y-center">
执行轮次<el-tooltip effect="dark" placement="top" hide-after="0">
<template #content>循环多少次-1为无限循环</template>
<SvgIcon name="ele-InfoFilled" class="ml5" />
</el-tooltip>
</div>
</template>
<el-input-number v-model="form.round" :min="-1" :disabled="form.interval === 21" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
<el-form-item label="定时类型" prop="interval" :rules="[{ required: true, message: '请选择定时类型', trigger: ['change'] }]">
<el-select v-model="form.interval" style="width: 150px" @change="onIntervalChange">
<el-option v-for="item in state.intervals" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-space fill class="w100">
<el-form-item
label="定时参数"
prop="intervalArgument"
:rules="[{ required: true, message: '请输入定时参数', trigger: ['blur', 'change'] }]"
>
<el-input v-model="form.intervalArgument" clearable>
<template #append v-if="form.interval === 21">
<el-button icon="ele-Clock" @click="onOpenCronDialog" />
</template>
</el-input>
</el-form-item>
<el-alert v-if="form.interval === 1" type="info" :closable="false">
设置 5 则每5秒触发执行N次
<br />
设置 5, 5, 10, 10, 60, 60 则每次按不同的间隔秒数触发执行6次
</el-alert>
<el-alert v-else-if="form.interval === 11" type="info" :closable="false"> 设置 08:00:00 则每天 08:00:00 触发执行N次 </el-alert>
<el-alert v-else-if="form.interval === 12" type="info" :closable="false">
设置 1:08:00:00 则每周一 08:00:00 触发
<br />
设置 0:08:00:00 则每周日 08:00:00 触发
</el-alert>
<el-alert v-else-if="form.interval === 13" type="info" :closable="false">
设置 1:08:00:00 则每月1日 08:00:00 触发
<br />
设置 -1:08:00:00 则每月最后一日 08:00:00 触发
</el-alert>
<el-alert v-else-if="form.interval === 21" type="info" :closable="false">
设置 0/10 * * * * ? 则从0秒开始每10秒执行一次
<br />
<pre style="line-height: 20px">
new FreeSchedulerBuilder()
...
.UseCustomInterval(task =>
{
// cron task.IntervalArgument
// TimeSpan null
return TimeSpan.FromSeconds(5);
})
.Build();
</pre
>
</el-alert>
</el-space>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel"> </el-button>
<el-button type="primary" @click="onSure" :loading="state.sureLoading"> </el-button>
</span>
</template>
</el-dialog>
<MyCronDialog ref="myCronDialogRef" @fill="onFillCron"></MyCronDialog>
<JsonEditorDialog ref="jsonEditorDialogRef" @sure="onSureArgs"></JsonEditorDialog>
</div>
</template>
<script lang="ts" setup name="admin/task/form">
import { reactive, toRefs, ref, defineAsyncComponent } from 'vue'
import { TaskUpdateInput } from '/@/api/admin/data-contracts'
import { TaskApi } from '/@/api/admin/Task'
import { cloneDeep } from 'lodash-es'
import eventBus from '/@/utils/mitt'
const MyCronDialog = defineAsyncComponent(() => import('/@/components/my-cron/dialog.vue'))
const JsonEditorDialog = defineAsyncComponent(() => import('./json-editor-dialog.vue'))
defineProps({
title: {
type: String,
default: '',
},
})
const formRef = ref()
const myCronDialogRef = ref()
const jsonEditorDialogRef = ref()
const state = reactive({
showDialog: false,
sureLoading: false,
form: {} as TaskUpdateInput,
intervals: [
{ label: '按秒触发', value: 1 },
{ label: '每天', value: 11 },
{ label: '每周几', value: 12 },
{ label: '每月第几日', value: 13 },
{ label: 'Cron表达式', value: 21 },
],
})
const { form } = toRefs(state)
//Cron
const onFillCron = (value: any) => {
form.value.intervalArgument = value
}
//
const onSureArgs = (task: any) => {
form.value.topic = task.topic
form.value.body = task.body
}
//
const open = async (row: TaskUpdateInput = { id: '' }) => {
let formData = cloneDeep(row) as TaskUpdateInput
if (row.id) {
const res = await new TaskApi().get({ id: row.id }, { loading: true })
if (res?.success) {
formData = res.data as TaskUpdateInput
}
}
state.form = formData
state.showDialog = true
}
//Cron
const onOpenCronDialog = () => {
myCronDialogRef.value.open(state.form.intervalArgument)
}
//Json
const onOpenJson = () => {
jsonEditorDialogRef.value.open(state.form)
}
//
const onCancel = () => {
state.showDialog = false
}
//
const onSure = () => {
formRef.value.validate(async (valid: boolean) => {
if (!valid) return
state.sureLoading = true
let res = {} as any
if (state.form.id) {
res = await new TaskApi().update(state.form, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
} else {
res = await new TaskApi().add(state.form, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
}
state.sureLoading = false
if (res?.success) {
eventBus.emit('refreshTask')
state.showDialog = false
}
})
}
const onIntervalChange = () => {
state.form.intervalArgument = ''
if (state.form.interval === 21) state.form.round = -1
}
defineExpose({
open,
})
</script>
<style scoped lang="scss">
.el-alert {
border-width: 0px !important;
margin-left: 110px;
margin-top: 10px;
}
</style>

View File

@ -1,133 +0,0 @@
<template>
<el-drawer v-model="state.showDialog" direction="ltr" :size="size">
<template #header="{ titleId, titleClass }">
<h4 :id="titleId" :class="titleClass">{{ title }}</h4>
<el-icon v-if="state.isFull" class="el-drawer__btn" @click="state.isFull = !state.isFull" title="还原"><ele-CopyDocument /></el-icon>
<el-icon v-else class="el-drawer__btn" @click="state.isFull = !state.isFull" title="最大化"><ele-FullScreen /></el-icon>
</template>
<div class="my-fill h100">
<el-table v-loading="state.loading" :data="state.taskLogListData" row-key="id" style="width: 100%">
<el-table-column prop="round" label="当前次数" width="90" />
<el-table-column prop="success" label="状态" width="90">
<template #default="{ row }">
<el-tag v-if="!row.success" type="danger" disable-transitions>失败</el-tag>
<el-tag v-else type="success" disable-transitions>成功</el-tag>
</template>
</el-table-column>
<el-table-column prop="elapsedMilliseconds" label="耗时(ms)" width="100" />
<el-table-column prop="exception" label="异常" min-width="180" />
<el-table-column prop="createTime" label="创建时间" :formatter="formatterTime" width="160" />
<el-table-column prop="remark" label="备注" min-width="180" />
</el-table>
<div class="my-flex my-flex-end" style="margin-top: 20px; padding: 0px 10px">
<el-pagination
v-model:currentPage="state.pageInput.currentPage"
v-model:page-size="state.pageInput.pageSize"
:total="state.total"
:page-sizes="[10, 20, 50, 100]"
background
@size-change="onSizeChange"
@current-change="onCurrentChange"
layout="total, sizes, prev, pager, next, jumper"
/>
</div>
</div>
<template #footer>
<div style="flex: auto; padding: 20px !important">
<el-button @click="onQuery" type="primary"> </el-button>
<el-button @click="onCancel"> </el-button>
</div>
</template>
</el-drawer>
</template>
<script lang="ts" setup name="admin/taskLog">
import { reactive, computed } from 'vue'
import { ResultOutputPageOutputTaskLog, PageInputTaskLogGetPageInput, TaskGetPageOutput } from '/@/api/admin/data-contracts'
import { TaskLogApi } from '/@/api/admin/TaskLog'
import dayjs from 'dayjs'
defineProps({
title: {
type: String,
default: '',
},
})
const state = reactive({
showDialog: false,
loading: false,
isFull: false,
isMobile: document.body.clientWidth < 1000,
taskLogFormTitle: '',
total: 0,
pageInput: {
currentPage: 1,
pageSize: 20,
filter: {
taskId: null,
},
} as PageInputTaskLogGetPageInput,
taskLogListData: [] as Array<ResultOutputPageOutputTaskLog>,
})
const size = computed(() => {
return state.isMobile ? '100%' : state.isFull ? '100%' : '50%'
})
const formatterTime = (row: any, column: any, cellValue: any) => {
return dayjs(cellValue).format('YYYY-MM-DD HH:mm:ss')
}
const onQuery = async () => {
state.loading = true
const res = await new TaskLogApi().getPage(state.pageInput).catch(() => {
state.loading = false
})
state.taskLogListData = res?.data?.list ?? []
state.total = res?.data?.total ?? 0
state.loading = false
}
const onSizeChange = (val: number) => {
state.pageInput.currentPage = 1
state.pageInput.pageSize = val
onQuery()
}
const onCurrentChange = (val: number) => {
state.pageInput.currentPage = val
onQuery()
}
//
const open = (row: TaskGetPageOutput) => {
if (state.pageInput.filter) state.pageInput.filter.taskId = row.id
onQuery()
state.showDialog = true
}
//
const onCancel = () => {
state.showDialog = false
}
defineExpose({
open,
})
</script>
<style scoped lang="scss">
.my-drawer-body-padding {
padding: 10px;
}
.el-drawer__btn {
cursor: pointer;
margin-right: 8px;
&:hover {
color: var(--el-color-primary);
}
}
</style>

View File

@ -1,397 +0,0 @@
<template>
<my-layout>
<div class="my-query-box mt8" style="position: relative">
<el-card shadow="never" :body-style="{ paddingBottom: '0' }">
<el-form :inline="true" label-width="auto" :label-position="'left'" @submit.stop.prevent>
<el-form-item label="任务分组">
<el-select v-model="state.filter.groupName" :empty-values="[null, undefined]" style="width: 120px" @change="onQuery">
<el-option v-for="group in state.groupList" :key="group.name" :label="group.name" :value="group.value" />
</el-select>
</el-form-item>
<el-form-item label="任务名称">
<el-input v-model="state.filter.taskName" placeholder="任务名称" @keyup.enter="onQuery" />
</el-form-item>
<el-form-item label="任务状态">
<el-select v-model="state.filter.taskStatus" :empty-values="[null]" style="width: 120px" @change="onQuery">
<el-option v-for="status in state.statusList" :key="status.name" :label="status.name" :value="status.value" />
</el-select>
</el-form-item>
<el-form-item label="创建日期">
<MyDateRange v-model:startDate="state.filter.startAddTime" v-model:endDate="state.filter.endAddTime" style="width: 230px" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="ele-Search" @click="onQuery"> 查询 </el-button>
<el-button v-auth="'api:admin:task:add'" type="primary" icon="ele-Plus" @click="onAdd"> 新增 </el-button>
</el-form-item>
</el-form>
<div
v-show="rowSelectCount > 0"
class="my-flex my-flex-items-center pl10"
style="position: absolute; top: 0; bottom: 0; left: 0; right: 0; background-color: var(--el-bg-color)"
>
<el-text class="mx-1"
>已选中 <el-text class="mx-1" type="primary">{{ rowSelectCount }}</el-text> </el-text
>
<el-divider direction="vertical" />
<el-button v-auth="'api:admin:task:run'" icon="ele-Promotion" text type="primary" @click="onBatchRun">执行</el-button>
<el-divider direction="vertical" />
<el-button v-auth="'api:admin:task:pause'" icon="ele-CaretRight" text type="primary" @click="onBatchStart">启动</el-button>
<el-divider direction="vertical" />
<el-button v-auth="'api:admin:task:resume'" icon="ele-VideoPause" text type="primary" @click="onBatchPause">停止</el-button>
<el-divider direction="vertical" />
<el-button v-auth="'api:admin:task:delete'" icon="ele-Delete" text type="danger" @click="onBatchDelete">删除</el-button>
<el-button size="large" link @click="onClear" style="position: absolute; right: 6px; top: 6px">
<svgIcon name="ele-Close" size="18"></svgIcon>
</el-button>
</div>
</el-card>
</div>
<el-card class="my-fill mt8 el-card-table" shadow="never">
<el-table ref="tableRef" v-loading="state.loading" :data="state.taskListData" row-key="id" style="width: 100%" border>
<el-table-column type="selection" width="40" />
<el-table-column type="index" label="序号" width="60" :index="indexMethod" />
<el-table-column prop="topic" label="任务名称" min-width="260">
<template #default="{ row }">
<div>{{ row.id }}</div>
<div>{{ row.topic }}</div>
</template>
</el-table-column>
<el-table-column prop="status" label="任务状态" width="95">
<template #default="{ row }">
<el-tag v-if="row.status === 0 || row.status === 'Running'" disable-transitions>运行中</el-tag>
<el-tag v-if="row.status === 1 || row.status === 'Paused'" type="info" disable-transitions>停止</el-tag>
<el-tag v-if="row.status === 2 || row.status === 'Completed'" type="success" disable-transitions>完成</el-tag>
</template>
</el-table-column>
<el-table-column prop="round" label="运行次数" width="90" />
<el-table-column prop="currentRound" label="当前次数" width="90" />
<el-table-column prop="errorTimes" label="失败次数" width="90">
<template #default="{ row }">
<el-text class="mx-1" type="danger">{{ row.errorTimes }}</el-text>
</template>
</el-table-column>
<el-table-column prop="body" label="任务数据" min-width="260" />
<el-table-column prop="intervalArgument" label="定时参数" min-width="120">
<template #default="{ row }">
<div>{{ formatterInterval(row.interval) }}</div>
<div>{{ row.intervalArgument }}</div>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" :formatter="formatterTime" width="100" />
<el-table-column prop="lastRunTime" label="最后运行时间" :formatter="formatterTime" width="120" />
<el-table-column label="操作" width="210" fixed="right" header-align="center" align="center">
<template #default="{ row }">
<div class="my-flex">
<el-button v-auth="'api:admin:task-log:get-page'" icon="ele-Tickets" text type="primary" @click="onShowLogs(row)">日志</el-button>
<el-button v-auth="'api:admin:task:update'" icon="ele-Edit" text type="primary" @click="onUpdate(row)">修改</el-button>
<el-button v-auth="'api:admin:task:delete'" icon="ele-Delete" text type="danger" @click="onDelete(row)">删除</el-button>
</div>
<div class="my-flex">
<el-button v-auth="'api:admin:task:run'" icon="ele-Promotion" text type="primary" @click="onRun(row)">执行</el-button>
<el-button v-auth="'api:admin:task:update'" icon="ele-CopyDocument" text type="primary" @click="onCopy(row)"> 复制 </el-button>
<el-button
v-if="row.status === 1 || row.status === 'Paused'"
v-auth="'api:admin:task:pause'"
icon="ele-CaretRight"
text
type="primary"
@click="onStart(row)"
>
启动
</el-button>
<el-button
v-if="row.status === 0 || row.status === 'Running'"
v-auth="'api:admin:task:resume'"
icon="ele-VideoPause"
text
type="primary"
@click="onPause(row)"
>
停止
</el-button>
</div>
</template>
</el-table-column>
</el-table>
<div class="my-flex my-flex-end" style="margin-top: 10px">
<el-pagination
v-model:currentPage="state.pageInput.currentPage"
v-model:page-size="state.pageInput.pageSize"
:total="state.total"
:page-sizes="[10, 20, 50, 100]"
background
@size-change="onSizeChange"
@current-change="onCurrentChange"
layout="total, sizes, prev, pager, next, jumper"
/>
</div>
</el-card>
<task-logs ref="taskLogsRef" :title="state.taskLogsTitle"></task-logs>
<task-form ref="taskFormRef" :title="state.taskFormTitle"></task-form>
</my-layout>
</template>
<script lang="ts" setup name="admin/task">
import { ref, reactive, onMounted, onBeforeMount, getCurrentInstance, defineAsyncComponent, computed } from 'vue'
import { ElTable, ElMessage } from 'element-plus'
import { TaskGetPageOutput, PageInputTaskGetPageInput, TaskStatus } from '/@/api/admin/data-contracts'
import { TaskApi } from '/@/api/admin/Task'
import dayjs from 'dayjs'
import eventBus from '/@/utils/mitt'
import { cloneDeep } from 'lodash-es'
//
const TaskLogs = defineAsyncComponent(() => import('./components/task-logs.vue'))
const TaskForm = defineAsyncComponent(() => import('./components/task-form.vue'))
const MyDateRange = defineAsyncComponent(() => import('/@/components/my-date-range/index.vue'))
const { proxy } = getCurrentInstance() as any
const taskLogsRef = ref()
const taskFormRef = ref()
const tableRef = ref<InstanceType<typeof ElTable>>()
const state = reactive({
loading: false,
taskFormTitle: '',
filter: {
taskName: '',
groupName: '',
taskStatus: undefined as TaskStatus | undefined,
startAddTime: undefined,
endAddTime: undefined,
},
total: 0,
pageInput: {
currentPage: 1,
pageSize: 20,
} as PageInputTaskGetPageInput,
taskListData: [] as Array<TaskGetPageOutput>,
taskLogsTitle: '',
groupList: [{ name: '全部', value: '' }],
statusList: [
{ name: '全部', value: undefined },
{ name: '运行中', value: 0 },
{ name: '停止', value: 1 },
{ name: '已完成', value: 2 },
],
})
onMounted(() => {
onQuery()
eventBus.off('refreshTask')
eventBus.on('refreshTask', async () => {
onQuery()
})
})
onBeforeMount(() => {
eventBus.off('refreshTask')
})
const rowSelectCount = computed(() => {
return tableRef.value?.getSelectionRows().length
})
const taskIds = computed(() => {
return tableRef.value?.getSelectionRows().map((a: any) => a.id)
})
const indexMethod = (index: number) => {
if (state.pageInput.currentPage && state.pageInput.pageSize) return index + 1 + (state.pageInput.currentPage - 1) * state.pageInput.pageSize
else return index
}
const formatterInterval = (cellValue: any) => {
let label = ''
switch (cellValue) {
case 1:
case 'SEC':
label = '按秒触发'
break
case 11:
case 'RunOnDay':
label = '每天'
break
case 12:
case 'RunOnWeek':
label = '每周几'
break
case 13:
case 'RunOnMonth':
label = '每月第几日'
break
case 21:
case 'Custom':
label = 'Cron表达式'
break
}
return label
}
const formatterTime = (row: any, column: any, cellValue: any) => {
return dayjs(cellValue).format('YYYY-MM-DD HH:mm:ss')
}
const onClear = () => {
tableRef.value?.clearSelection()
}
const onQuery = async () => {
state.loading = true
state.pageInput.filter = state.filter
const res = await new TaskApi().getPage(state.pageInput).catch(() => {
state.loading = false
})
state.taskListData = res?.data?.list ?? []
state.total = res?.data?.total ?? 0
state.loading = false
}
const onAdd = () => {
state.taskFormTitle = '新增任务'
taskFormRef.value.open()
}
const onUpdate = (row: TaskGetPageOutput) => {
state.taskFormTitle = '修改任务'
taskFormRef.value.open(row)
}
const onCopy = (row: TaskGetPageOutput) => {
state.taskFormTitle = '新增任务'
var task = cloneDeep(row)
task.id = null
taskFormRef.value.open(task)
}
//
const onShowLogs = (row: TaskGetPageOutput) => {
state.taskLogsTitle = `${row.topic}${row.id}运行日志`
taskLogsRef.value.open(row)
}
const onRun = (row: TaskGetPageOutput) => {
proxy.$modal
.confirm(`确定要运行【${row.topic}】任务?`)
.then(async () => {
await new TaskApi().run({ id: row.id as string }, { loading: true, showSuccessMessage: true })
onQuery()
})
.catch(() => {})
}
const onPause = (row: TaskGetPageOutput) => {
proxy.$modal
.confirm(`确定要停止【${row.topic}】任务?`)
.then(async () => {
await new TaskApi().pause({ id: row.id as string }, { loading: true, showSuccessMessage: true })
onQuery()
})
.catch(() => {})
}
const onStart = (row: TaskGetPageOutput) => {
proxy.$modal
.confirm(`确定要启动【${row.topic}】任务?`)
.then(async () => {
await new TaskApi().resume({ id: row.id as string }, { loading: true, showSuccessMessage: true })
onQuery()
})
.catch(() => {})
}
const onDelete = (row: TaskGetPageOutput) => {
proxy.$modal
.confirmDelete(`确定要删除【${row.topic}】任务?`)
.then(async () => {
await new TaskApi().delete({ id: row.id as string }, { loading: true, showSuccessMessage: true })
onQuery()
})
.catch(() => {})
}
const checkRowSelect = () => {
if (rowSelectCount.value > 0) {
return true
} else {
ElMessage({
message: '请选择任务再操作',
type: 'warning',
})
return false
}
}
const onBatchRun = () => {
if (!checkRowSelect()) {
return
}
proxy.$modal
.confirm(`确定要运行 ${rowSelectCount.value} 项任务?`)
.then(async () => {
await new TaskApi().batchRun(taskIds.value, { loading: true, showSuccessMessage: true })
onQuery()
})
.catch(() => {})
}
const onBatchPause = () => {
if (!checkRowSelect()) {
return
}
proxy.$modal
.confirm(`确定要停止 ${rowSelectCount.value} 项任务?`)
.then(async () => {
await new TaskApi().batchPause(taskIds.value, { loading: true, showSuccessMessage: true })
onQuery()
})
.catch(() => {})
}
const onBatchStart = () => {
if (!checkRowSelect()) {
return
}
proxy.$modal
.confirm(`确定要启动 ${rowSelectCount.value} 项任务?`)
.then(async () => {
await new TaskApi().batchResume(taskIds.value, { loading: true, showSuccessMessage: true })
onQuery()
})
.catch(() => {})
}
const onBatchDelete = () => {
if (!checkRowSelect()) {
return
}
proxy.$modal
.confirm(`确定要删除 ${rowSelectCount.value} 项任务?`)
.then(async () => {
await new TaskApi().batchDelete(taskIds.value, { loading: true, showSuccessMessage: true })
onQuery()
})
.catch(() => {})
}
const onSizeChange = (val: number) => {
state.pageInput.currentPage = 1
state.pageInput.pageSize = val
onQuery()
}
const onCurrentChange = (val: number) => {
state.pageInput.currentPage = val
onQuery()
}
</script>

View File

@ -1,152 +0,0 @@
<template>
<div>
<el-dialog
v-model="state.showDialog"
destroy-on-close
title="加载共享文件夹"
draggable
:close-on-click-modal="false"
:close-on-press-escape="false"
width="600px"
>
<el-form :model="form" ref="formRef" label-width="120px">
<el-alert
v-if="state.isReadonly"
title="当前为指定文件夹加载模式,父级文件夹和文件路径已自动设置"
type="info"
:closable="false"
style="margin-bottom: 20px"
/>
<el-row :gutter="35">
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="父级文件夹">
<el-input
v-if="state.isReadonly"
v-model="state.parentName"
readonly
placeholder="根目录"
class="w100"
/>
<el-tree-select
v-else
v-model="form.parentId"
:data="templateTreeData.filter(item => (item.type || 1) === 1)"
node-key="id"
check-strictly
default-expand-all
render-after-expand
fit-input-width
clearable
:empty-values="[0, null, undefined]"
class="w100"
:props="{ label: 'name', value: 'id' }"
placeholder="请选择父级文件夹,为空则表示根目录"
/>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="文件夹路径" prop="folderPath" :rules="[{ required: true, message: '请输入文件夹路径', trigger: ['blur', 'change'] }]">
<el-input
v-model="form.folderPath"
:readonly="state.isReadonly"
clearable
placeholder="请输入共享文件夹路径"
/>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="包含子文件夹">
<el-switch v-model="form.includeSubfolders" />
<el-text type="info" size="small" style="margin-left: 10px">
是否包含子文件夹内容
</el-text>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel"> </el-button>
<el-button type="primary" @click="onSure" :loading="state.sureLoading">开始加载</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup name="admin/templatecenter/folder-load-form">
import { reactive, toRefs, getCurrentInstance, ref, PropType } from 'vue'
import { TemplateCenterGetTreeOutput } from '/@/api/admin/TemplateCenter'
import { TemplateCenterApi } from '/@/api/admin/TemplateCenter'
import eventBus from '/@/utils/mitt'
defineProps({
templateTreeData: {
type: Array as PropType<TemplateCenterGetTreeOutput[]>,
default: () => [],
},
})
const { proxy } = getCurrentInstance() as any
const formRef = ref()
const state = reactive({
showDialog: false,
sureLoading: false,
isReadonly: false,
parentName: '',
form: {
parentId: undefined as number | undefined,
folderPath: '',
includeSubfolders: false,
},
})
const { form } = toRefs(state)
//
const open = async (parentId?: number, parentName?: string, folderPath?: string) => {
state.isReadonly = !!(parentId && parentName) //
state.parentName = parentName || ''
state.form = {
parentId: parentId || undefined,
folderPath: folderPath || '',
includeSubfolders: false,
}
state.showDialog = true
}
//
const onCancel = () => {
state.showDialog = false
}
//
const onSure = () => {
formRef.value.validate(async (valid: boolean) => {
if (!valid) return
state.sureLoading = true
const res = await new TemplateCenterApi().getSharedFolderContent({
folderPath: state.form.folderPath,
parentId: state.form.parentId && state.form.parentId > 0 ? state.form.parentId : undefined,
includeSubfolders: state.form.includeSubfolders,
}, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
state.sureLoading = false
if (res?.success) {
eventBus.emit('refreshTemplateCenter')
state.showDialog = false
}
})
}
defineExpose({
open,
})
</script>
<style scoped lang="scss"></style>

View File

@ -1,427 +0,0 @@
<template>
<el-dialog v-model="state.isShowDialog" title="关键词生成助手" width="800px" destroy-on-close>
<div class="keyword-helper">
<!-- 使用说明 -->
<el-alert
title="使用说明"
description="支持格式:通用关键词&后缀|特定关键词&后缀,如:报告模板,报告信息&doc,docx|报表&xlsx,xls|归档&pdf"
type="info"
:closable="false"
style="margin-bottom: 20px"
/>
<el-row :gutter="20">
<!-- 左侧关键词配置 -->
<el-col :span="12">
<el-card header="关键词配置" shadow="never">
<!-- 通用关键词 -->
<div class="section">
<h4>通用关键词</h4>
<el-input
v-model="state.commonKeywords"
placeholder="输入通用关键词,用逗号分隔"
type="textarea"
:rows="2"
/>
</div>
<!-- 特定后缀关键词 -->
<div class="section">
<h4>
特定后缀关键词
<el-button type="primary" link size="small" @click="addExtensionGroup">
<el-icon><Plus /></el-icon>
添加组合
</el-button>
</h4>
<div v-for="(group, index) in state.extensionGroups" :key="index" class="extension-group">
<el-row :gutter="10" align="middle">
<el-col :span="10">
<el-input
v-model="group.keywords"
placeholder="关键词,用逗号分隔"
size="small"
/>
</el-col>
<el-col :span="2" class="text-center">
<span>&</span>
</el-col>
<el-col :span="10">
<el-select
v-model="group.extensions"
placeholder="选择文件后缀"
multiple
collapse-tags
collapse-tags-tooltip
size="small"
style="width: 100%"
>
<el-option-group label="文档类">
<el-option label="doc" value="doc" />
<el-option label="docx" value="docx" />
<el-option label="pdf" value="pdf" />
<el-option label="txt" value="txt" />
<el-option label="md" value="md" />
</el-option-group>
<el-option-group label="表格类">
<el-option label="xlsx" value="xlsx" />
<el-option label="xls" value="xls" />
<el-option label="csv" value="csv" />
</el-option-group>
<el-option-group label="演示文稿">
<el-option label="ppt" value="ppt" />
<el-option label="pptx" value="pptx" />
</el-option-group>
<el-option-group label="图片类">
<el-option label="jpg" value="jpg" />
<el-option label="jpeg" value="jpeg" />
<el-option label="png" value="png" />
<el-option label="gif" value="gif" />
</el-option-group>
<el-option-group label="压缩类">
<el-option label="zip" value="zip" />
<el-option label="rar" value="rar" />
<el-option label="7z" value="7z" />
</el-option-group>
<el-option-group label="代码类">
<el-option label="js" value="js" />
<el-option label="ts" value="ts" />
<el-option label="vue" value="vue" />
<el-option label="html" value="html" />
<el-option label="css" value="css" />
</el-option-group>
</el-select>
</el-col>
<el-col :span="2">
<el-button
type="danger"
link
size="small"
@click="removeExtensionGroup(index)"
>
<el-icon><Delete /></el-icon>
</el-button>
</el-col>
</el-row>
</div>
</div>
<!-- 快速模板 -->
<div class="section">
<h4>快速模板</h4>
<el-space wrap>
<el-button
v-for="template in templates"
:key="template.name"
size="small"
@click="applyTemplate(template)"
>
{{ template.name }}
</el-button>
</el-space>
</div>
</el-card>
</el-col>
<!-- 右侧预览和生成 -->
<el-col :span="12">
<el-card header="预览和生成" shadow="never">
<div class="section">
<h4>生成结果</h4>
<el-input
v-model="generatedKeywords"
type="textarea"
:rows="6"
readonly
placeholder="配置左侧选项后,将在此显示生成的关键词"
/>
<div style="margin-top: 10px; text-align: right">
<el-button
type="primary"
size="small"
:disabled="!generatedKeywords"
@click="copyToClipboard"
>
<el-icon><CopyDocument /></el-icon>
复制到剪贴板
</el-button>
</div>
</div>
<div class="section">
<h4>格式说明</h4>
<div class="format-example">
<p><strong>基本格式</strong></p>
<code>通用关键词&后缀|特定关键词&后缀</code>
<p><strong>示例解释</strong></p>
<div class="example-item">
<code>报告模板,报告信息&doc,docx</code>
<span> 在doc/docx文件中搜索"报告模板""报告信息"</span>
</div>
<div class="example-item">
<code>|报表&xlsx,xls</code>
<span> 在xlsx/xls文件中搜索"报表"</span>
</div>
<div class="example-item">
<code>|归档&pdf</code>
<span> 在pdf文件中搜索"归档"</span>
</div>
</div>
</div>
</el-card>
</el-col>
</el-row>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="state.isShowDialog = false">取消</el-button>
<el-button type="primary" @click="applyKeywords">应用关键词</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="KeywordHelper">
import { reactive, computed } from 'vue'
import { ElMessage } from 'element-plus'
import { Plus, Delete, CopyDocument } from '@element-plus/icons-vue'
//
interface ExtensionGroup {
keywords: string
extensions: string[]
}
interface Template {
name: string
commonKeywords: string
extensionGroups: ExtensionGroup[]
}
//
const state = reactive({
isShowDialog: false,
commonKeywords: '',
extensionGroups: [] as ExtensionGroup[]
})
//
const templates: Template[] = [
{
name: '办公文档',
commonKeywords: '文档,模板',
extensionGroups: [
{ keywords: '报告,总结', extensions: ['doc', 'docx'] },
{ keywords: '报表,统计', extensions: ['xlsx', 'xls'] },
{ keywords: '演示,汇报', extensions: ['ppt', 'pptx'] }
]
},
{
name: '项目管理',
commonKeywords: '项目,管理',
extensionGroups: [
{ keywords: '需求,方案', extensions: ['doc', 'docx', 'pdf'] },
{ keywords: '计划,进度', extensions: ['xlsx', 'xls'] },
{ keywords: '设计,原型', extensions: ['png', 'jpg', 'pdf'] }
]
},
{
name: '技术文档',
commonKeywords: '技术,开发',
extensionGroups: [
{ keywords: 'API,接口', extensions: ['md', 'pdf'] },
{ keywords: '代码,脚本', extensions: ['js', 'ts', 'vue'] },
{ keywords: '配置,部署', extensions: ['json', 'yml', 'xml'] }
]
},
{
name: '财务相关',
commonKeywords: '财务,会计',
extensionGroups: [
{ keywords: '报表,账单', extensions: ['xlsx', 'xls'] },
{ keywords: '合同,协议', extensions: ['doc', 'docx', 'pdf'] },
{ keywords: '发票,凭证', extensions: ['pdf', 'jpg', 'png'] }
]
}
]
//
const generatedKeywords = computed(() => {
const parts: string[] = []
//
if (state.commonKeywords.trim()) {
parts.push(state.commonKeywords.trim())
}
//
state.extensionGroups.forEach(group => {
if (group.keywords.trim() && group.extensions.length > 0) {
const keywordsPart = group.keywords.trim()
const extensionsPart = group.extensions.join(',')
parts.push(`${keywordsPart}&${extensionsPart}`)
}
})
return parts.join('|')
})
//
const addExtensionGroup = () => {
state.extensionGroups.push({
keywords: '',
extensions: []
})
}
//
const removeExtensionGroup = (index: number) => {
state.extensionGroups.splice(index, 1)
}
//
const applyTemplate = (template: Template) => {
state.commonKeywords = template.commonKeywords
state.extensionGroups = [...template.extensionGroups]
}
//
const copyToClipboard = async () => {
if (!generatedKeywords.value) return
try {
await navigator.clipboard.writeText(generatedKeywords.value)
ElMessage.success('已复制到剪贴板')
} catch (err) {
//
const textArea = document.createElement('textarea')
textArea.value = generatedKeywords.value
document.body.appendChild(textArea)
textArea.select()
document.execCommand('copy')
document.body.removeChild(textArea)
ElMessage.success('已复制到剪贴板')
}
}
//
const clearAll = () => {
state.commonKeywords = ''
state.extensionGroups = []
}
//
const emit = defineEmits<{
apply: [keywords: string]
}>()
//
const open = (currentKeywords?: string) => {
clearAll()
//
if (currentKeywords) {
parseExistingKeywords(currentKeywords)
}
state.isShowDialog = true
}
//
const parseExistingKeywords = (keywords: string) => {
const parts = keywords.split('|')
for (const part of parts) {
if (part.includes('&')) {
//
const [keywordsPart, extensionsPart] = part.split('&')
state.extensionGroups.push({
keywords: keywordsPart.trim(),
extensions: extensionsPart.split(',').map(e => e.trim())
})
} else {
//
state.commonKeywords = part.trim()
}
}
}
//
const applyKeywords = () => {
if (generatedKeywords.value) {
emit('apply', generatedKeywords.value)
state.isShowDialog = false
ElMessage.success('关键词已应用')
} else {
ElMessage.warning('请先配置关键词')
}
}
//
defineExpose({
open
})
</script>
<style scoped lang="scss">
.keyword-helper {
.section {
margin-bottom: 20px;
h4 {
margin: 0 0 10px 0;
font-size: 14px;
font-weight: 600;
color: #303133;
display: flex;
align-items: center;
justify-content: space-between;
}
}
.extension-group {
margin-bottom: 10px;
padding: 10px;
border: 1px solid #ebeef5;
border-radius: 4px;
background-color: #fafafa;
}
.text-center {
text-align: center;
font-weight: 600;
color: #909399;
}
.format-example {
font-size: 12px;
color: #606266;
p {
margin: 10px 0 5px 0;
}
code {
background-color: #f5f7fa;
padding: 2px 4px;
border-radius: 3px;
font-family: 'Courier New', monospace;
}
.example-item {
margin: 8px 0;
display: flex;
flex-direction: column;
gap: 4px;
span {
color: #909399;
font-size: 11px;
}
}
}
}
</style>

Some files were not shown because too many files have changed in this diff Show More