feat: 框架代码同步 (#150)

Co-authored-by: tk <fiyne1a@dingtalk.com>
This commit is contained in:
2024-07-03 22:09:58 +08:00
committed by GitHub
parent beba4124b0
commit e1b0030193
263 changed files with 2604 additions and 684 deletions

View File

@ -11,14 +11,14 @@
"dependencies": {
"@element-plus/icons-vue": "^2.3.1",
"ace-builds": "^1.35.0",
"aieditor": "^1.0.9",
"aieditor": "^1.0.10",
"axios": "^1.7.2",
"clipboard": "^2.0.11",
"core-js": "^3.37.1",
"cropperjs": "^1.6.2",
"crypto-js": "^4.2.0",
"echarts": "^5.5.0",
"element-plus": "^2.7.5",
"element-plus": "^2.7.6",
"json-bigint": "^1.0.0",
"json5-to-table": "^0.1.8",
"markdown-it": "^14.1.0",
@ -28,9 +28,9 @@
"qrcodejs2": "^0.0.2",
"sortablejs": "^1.15.2",
"vkbeautify": "^0.99.3",
"vue": "^3.4.29",
"vue": "^3.4.30",
"vue-i18n": "^9.13.1",
"vue-router": "^4.3.3",
"vue-router": "^4.4.0",
"vue3-ace-editor": "^2.2.4",
"vue3-json-viewer": "^2.2.2",
"vuedraggable": "^4.0.3",
@ -40,7 +40,7 @@
"@vitejs/plugin-vue": "^5.0.5",
"prettier": "^3.3.2",
"prettier-plugin-organize-attributes": "^1.0.0",
"sass": "^1.77.5",
"sass": "^1.77.6",
"terser": "^5.31.1",
"vite": "^5.3.1"
},

View File

@ -16,6 +16,17 @@ export default {
},
},
/**
* 导出接口
*/
export: {
url: `${config.API_URL}/api/sys/api/export`,
name: `导出接口`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
/**
* 查询接口
*/

View File

@ -60,6 +60,17 @@ export default {
},
},
/**
* 导出配置
*/
export: {
url: `${config.API_URL}/api/sys/config/export`,
name: `导出配置`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
/**
* 获取单个配置
*/

View File

@ -60,6 +60,17 @@ export default {
},
},
/**
* 导出部门
*/
export: {
url: `${config.API_URL}/api/sys/dept/export`,
name: `导出部门`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
/**
* 获取单个部门
*/

View File

@ -93,6 +93,17 @@ export default {
},
},
/**
* 导出字典内容
*/
exportContent: {
url: `${config.API_URL}/api/sys/dic/export.content`,
name: `导出字典内容`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
/**
* 获取单个字典目录
*/

View File

@ -27,6 +27,17 @@ export default {
},
},
/**
* 作业记录计数
*/
countRecord: {
url: `${config.API_URL}/api/sys/job/count.record`,
name: `作业记录计数`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
/**
* 创建计划作业
*/
@ -82,6 +93,28 @@ export default {
},
},
/**
* 导出计划作业
*/
export: {
url: `${config.API_URL}/api/sys/job/export`,
name: `导出计划作业`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
/**
* 导出作业记录
*/
exportRecord: {
url: `${config.API_URL}/api/sys/job/export.record`,
name: `导出作业记录`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
/**
* 获取单个计划作业
*/
@ -93,6 +126,17 @@ export default {
},
},
/**
* 获取单个作业记录
*/
getRecord: {
url: `${config.API_URL}/api/sys/job/get.record`,
name: `获取单个作业记录`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
/**
* 获取作业记录条形图数据
*/
@ -137,6 +181,17 @@ export default {
},
},
/**
* 分页查询作业记录
*/
pagedQueryRecord: {
url: `${config.API_URL}/api/sys/job/paged.query.record`,
name: `分页查询作业记录`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
/**
* 查询计划作业
*/
@ -148,28 +203,6 @@ export default {
},
},
/**
* 获取单个作业记录
*/
recordGet: {
url: `${config.API_URL}/api/sys/job/record.get`,
name: `获取单个作业记录`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
/**
* 分页查询作业记录
*/
recordPagedQuery: {
url: `${config.API_URL}/api/sys/job/record.paged.query`,
name: `分页查询作业记录`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
/**
* 启用/禁用作业
*/

View File

@ -16,6 +16,17 @@ export default {
},
},
/**
* 导出请求日志
*/
export: {
url: `${config.API_URL}/api/sys/log/export`,
name: `导出请求日志`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
/**
* 获取单个请求日志
*/

View File

@ -60,6 +60,17 @@ export default {
},
},
/**
* 导出角色
*/
export: {
url: `${config.API_URL}/api/sys/role/export`,
name: `导出角色`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
/**
* 获取单个角色
*/

View File

@ -71,6 +71,17 @@ export default {
},
},
/**
* 导出站内信
*/
export: {
url: `${config.API_URL}/api/sys/site.msg/export`,
name: `导出站内信`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
/**
* 获取单个站内信
*/

View File

@ -71,6 +71,17 @@ export default {
},
},
/**
* 导出用户
*/
export: {
url: `${config.API_URL}/api/sys/user/export`,
name: `导出用户`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
/**
* 获取单个用户
*/

View File

@ -60,6 +60,17 @@ export default {
},
},
/**
* 导出示例
*/
export: {
url: `${config.API_URL}/api/tpl/example/export`,
name: `导出示例`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
/**
* 获取单个示例
*/

View File

@ -1,9 +1,9 @@
<template>
<sc-table-select
v-model="area"
:apiObj="$API.sys_dic.pagedQueryContent"
:params="form"
:props="{ label: 'key', value: 'value' }"
:query-api="$API.sys_dic.pagedQueryContent"
:table-width="60"
clearable
ref="area">

View File

@ -25,10 +25,10 @@
v-else-if="item.type === 'remote-select' && (!item.condition || item.condition())"
v-model="form[item.field[0]][item.field[1]]"
v-role="item.role || '*/*/*'"
:apiObj="item.api"
:class="item.class"
:config="item.config"
:placeholder="item.placeholder"
:query-api="item.api"
:style="item.style"
clearable
filterable />
@ -321,9 +321,7 @@ export default {
},
],
casLoaded: false,
keepKeywords: null,
keepCreatedTime: null,
keepHttpStatusCode: null,
keeps: [],
form: {
root: {},
filter: {},
@ -399,14 +397,8 @@ export default {
Object.keys(this.form.dy).forEach((x) => {
delete this.form.dy[x]
})
if (this.keepKeywords) {
this.form.root.keywords = this.keepKeywords
}
if (this.keepCreatedTime) {
this.form.dy.createdTime = this.keepCreatedTime
}
if (this.keepHttpStatusCode) {
this.form.dy.httpStatusCode = this.keepHttpStatusCode
for (const keep of this.keeps) {
this.form[keep.type][keep.field] = keep.value
}
this.$emit('reset')
this.search()

View File

@ -1,9 +1,9 @@
<template>
<sc-table-select
v-model="user"
:apiObj="$API.sys_user.pagedQuery"
:params="form"
:props="{ label: 'userName', value: 'id' }"
:query-api="$API.sys_user.pagedQuery"
:table-width="60"
clearable
ref="user">

View File

@ -31,7 +31,7 @@ export default {
option: {
deep: true,
handler(v) {
unwarp(this.myChart).setOption(v)
unwarp(this.myChart).setOption(v, { notMerge: true })
},
},
},

View File

@ -68,7 +68,22 @@
background></el-pagination>
</div>
<div v-if="!hideDo" class="scTable-do">
<el-button v-if="!hideRefresh" @click="refresh" circle icon="el-icon-refresh" style="margin-left: 1rem"></el-button>
<el-button
v-if="exportApi"
:title="$t('导出文件')"
@click="exportData"
circle
icon="el-icon-download"
plain
style="margin-left: 1rem"
type="primary"></el-button>
<el-button
v-if="!hideRefresh"
:title="$t('刷新')"
@click="refresh"
circle
icon="el-icon-refresh"
style="margin-left: 1rem"></el-button>
<el-popover
v-if="column"
:hide-after="0"
@ -182,7 +197,8 @@
:key="index"
:title="adv.label">
</sc-contextmenu-item>
<sc-contextmenu-item :title="$t('重新加载')" command="refresh" divided icon="el-icon-refresh" suffix="Ctrl+R"></sc-contextmenu-item>
<sc-contextmenu-item v-if="exportApi" :title="$t('导出文件')" command="export" divided icon="el-icon-download"></sc-contextmenu-item>
<sc-contextmenu-item :title="$t('重新加载')" command="refresh" icon="el-icon-refresh" suffix="Ctrl+R"></sc-contextmenu-item>
</sc-contextmenu>
</template>
<script>
@ -209,10 +225,14 @@ export default {
beforePost: {
type: Function,
},
apiObj: {
queryApi: {
type: Object,
default: () => {},
},
exportApi: {
type: Object,
default: null,
},
params: { type: Object, default: () => ({}) },
data: {
type: Object,
@ -229,6 +249,7 @@ export default {
summaryMethod: { type: Function, default: null },
filterMethod: { type: Function, default: null },
cellClickMethod: { type: Function, default: null },
onCommand: { type: Function, default: null },
column: {
type: Object,
default: () => {},
@ -252,7 +273,7 @@ export default {
this.tableData = this.data
this.total = this.tableData.length
},
apiObj() {
queryApi() {
this.tableParams = this.params
this.refresh()
},
@ -310,7 +331,7 @@ export default {
this.userColumn = this.column
}
//判断是否静态数据
if (this.apiObj) {
if (this.queryApi) {
this.getData()
} else if (this.data) {
this.tableData = this.data
@ -335,15 +356,15 @@ export default {
return
}
if (command === 'view') {
this.vue.dialog.save = true
await this.$nextTick()
await this.vue.$refs.saveDialog.open('view', { id: this.current.row.id })
this.vue.dialog.save = { mode: 'view', row: { id: this.current.row.id } }
return
}
if (command === 'export') {
this.exportData()
return
}
if (command === 'edit') {
this.vue.dialog.save = true
await this.$nextTick()
await this.vue.$refs.saveDialog.open('edit', { id: this.current.row.id })
this.vue.dialog.save = { mode: 'edit', row: { id: this.current.row.id } }
return
}
if (command === 'del') {
@ -376,6 +397,9 @@ export default {
operator: kv[1],
value: value.length === 1 ? value[0] : value,
})
if (this.onCommand) {
this.onCommand(this.vue.query.dynamicFilter.filters)
}
this.upData()
},
contextMenuVisibleChange(visible) {
@ -395,9 +419,7 @@ export default {
async getCustomColumn() {
this.userColumn = await config.columnSettingGet(this.tableName, this.column)
},
//获取数据
async getData() {
this.loading = true
getQueryParams() {
const reqData = {
[config.request.page]: this.currentPage,
[config.request.pageSize]: this.scPageSize,
@ -409,10 +431,17 @@ export default {
delete reqData[config.request.pageSize]
}
Object.assign(reqData, this.tableParams)
return reqData
},
//获取数据
async getData() {
this.loading = true
let res
let response
try {
if (!this.beforePost || this.beforePost(reqData)) res = await this.apiObj.post(reqData)
const reqData = this.getQueryParams()
if (!this.beforePost || this.beforePost(reqData)) res = await this.queryApi.post(reqData)
} catch (error) {
this._clearData()
this.loading = false
@ -463,6 +492,15 @@ export default {
this.$refs.scTable.clearSelection()
this.getData()
},
//导出数据
async exportData() {
this.loading = true
try {
await this.exportApi.post(this.getQueryParams())
this.$message.success(`数据已导出上限1万条`)
} catch {}
this.loading = false
},
//更新数据 合并上一次params
upData(params = null, page = 1) {
this.currentPage = page

View File

@ -9,7 +9,7 @@
在处理耗时过久的作业时为了不阻碍正在处理的工作可在作业中心进行异步执行
</p>
</el-empty>
<el-card v-for="job in jobs" :class="`user-bar-jobs-item ${job.lastStatusCode === 'ok' ? '' : 'alert'}`" :key="job.id" shadow="hover">
<el-card v-for="job in jobs" :class="`user-bar-jobs-item ${job.lastStatusCode === 'oK' ? '' : 'alert'}`" :key="job.id" shadow="hover">
<div class="user-bar-jobs-item-body">
<div class="jobIcon">
{{ job.lastStatusCode }}
@ -25,13 +25,13 @@
<div class="bottom">
<div class="status">
<el-tag v-if="job.status === 'running'" type="warning">{{ $t('执行中') }}</el-tag>
<el-tag v-if="job.status === 'idle'" :type="job.lastStatusCode === 'ok' ? 'primary' : 'danger'">{{
<el-tag v-if="job.status === 'idle'" :type="job.lastStatusCode === 'oK' ? 'primary' : 'danger'">{{
$t('空闲')
}}</el-tag>
</div>
<div class="handler">
<el-button
:type="job.lastStatusCode === 'ok' ? 'primary' : 'danger'"
:type="job.lastStatusCode === 'oK' ? 'primary' : 'danger'"
@click="view(job)"
circle
icon="el-icon-view"></el-button>
@ -41,8 +41,15 @@
</div>
</el-card>
</el-main>
<el-footer style="text-align: right">
<el-button @click="refresh" circle icon="el-icon-refresh"></el-button>
<el-footer class="flex" style="justify-content: space-evenly; height: unset">
<div>
<el-badge :hidden="fail === 0" :value="fail">
<el-button @click="gotoJob">{{ $t('异常作业') }}</el-button>
</el-badge>
</div>
<div style="text-align: right; flex-grow: 1">
<el-button @click="refresh" circle icon="el-icon-refresh"></el-button>
</div>
</el-footer>
</el-container>
@ -63,8 +70,13 @@ export default {
jobs: [],
}
},
emits: ['closed'],
inject: ['reload'],
methods: {
gotoJob() {
this.$router.push({ path: '/sys/job', query: { view: 'fail' } })
this.$emit('closed')
},
async getData() {
this.loading = true
const res = await this.$API.sys_job.query.post({ prop: 'lastStatusCode', order: 'descending' })
@ -81,7 +93,9 @@ export default {
mounted() {
this.getData()
},
props: [],
props: {
fail: { type: Number, default: 0 },
},
watch: {},
}
</script>

View File

@ -16,9 +16,11 @@
</el-icon>
</div>
<div v-auth="'sys/job/userbar'" @click="tasks" class="tasks panel-item">
<el-icon>
<sc-icon-ScheduledJob />
</el-icon>
<el-badge :hidden="failJobCnt === 0" :value="failJobCnt">
<el-icon>
<sc-icon-ScheduledJob />
</el-icon>
</el-badge>
</div>
<div @click="showMsg" class="msg panel-item">
<el-badge :hidden="unreadCnt === 0" :value="unreadCnt" class="badge" type="danger">
@ -73,7 +75,7 @@
</el-dialog>
<el-drawer v-model="tasksVisible" :size="450" :title="$t('作业中心')" destroy-on-close>
<tasks></tasks>
<tasks :fail="failJobCnt" @closed="tasksVisible = false"></tasks>
</el-drawer>
</template>
@ -96,8 +98,51 @@ export default {
},
async created() {
this.user = this.$GLOBAL.user
const res = await this.$API.sys_sitemsg.unreadCount.post()
let res = await this.$API.sys_sitemsg.unreadCount.post()
this.unreadCnt = res.data
if (this.$GLOBAL.permissions.some((x) => x === '*/*/*' || x === 'sys/job/userbar')) {
res = await this.$API.sys_job.countRecord.post({
dynamicFilter: {
filters: [
{
field: 'createdTime',
operator: 'dateRange',
value: [
this.$TOOL.data.get('APP_SET_FAIL_JOB_VIEW_TIME') ?? this.$TOOL.dateFormat(new Date(), 'yyyy-MM-dd'),
this.$TOOL.dateFormat(new Date(), 'yyyy-MM-dd hh:mm:ss'),
],
},
{
logic: 'or',
filters: [
{
field: 'httpStatusCode',
operator: 'range',
value: '300,399',
},
{
field: 'httpStatusCode',
operator: 'range',
value: '400,499',
},
{
field: 'httpStatusCode',
operator: 'range',
value: '500,599',
},
{
field: 'httpStatusCode',
operator: 'range',
value: '900,999',
},
],
},
],
},
})
this.failJobCnt = res.data
}
},
data() {
return {
@ -111,6 +156,7 @@ export default {
tasksVisible: false,
msg: false,
unreadCnt: 0,
failJobCnt: 0,
}
},
methods: {

View File

@ -43,12 +43,10 @@ export default {
住宅地址: 'Residential address',
住宅电话: 'Residential phones',
作业中心: 'Job Center',
作业分布: 'Job distribution',
作业名称: 'Job name',
作业名称不能为空: 'The job name cannot be empty',
作业状态: 'Status',
作业编号: 'Job ID',
作业趋势: 'Job trends',
保存: 'Save',
修改密码: 'Change your password',
修改时间: 'Modify time',
@ -251,8 +249,6 @@ export default {
角色名称: 'Role name',
角色编号: 'Role ID',
设置邮箱: 'Set email',
访问分布: 'Access distribution',
访问趋势: 'Access trends',
证件号码: 'ID number',
证件类型: 'ID type',
请再次输入新密码: 'Please enter the new password again',
@ -472,4 +468,20 @@ export default {
: 'Yes',
: 'No',
手机: 'Mobile',
'您已退出登录或无权限访问当前资源,请重新登录后再操作':
'You have logged out or do not have permission to access the current resource, please log in again before operating',
访问受限: 'Access restricted',
请求错误: 'Request error',
'正在请求不存在的服务器记录!': 'Requesting a non-existent server record!',
'服务器发生错误!': 'Server error!',
'未知错误!': 'Unknown error!',
'请求服务器无响应!': 'Request server no response!',
仪表板布局: 'Dashboard layout',
应用配置: 'Application configuration',
导出文件: 'Export file',
刷新: 'Refresh',
'访问分布(Today)': 'Access distribution (Today)',
'访问趋势(Today)': 'Access trend (Today)',
'作业分布(Today)': 'Job distribution (Today)',
'作业趋势(Today)': 'Job trend (Today)',
}

View File

@ -43,12 +43,10 @@ export default {
住宅地址: '住宅地址',
住宅电话: '住宅电话',
作业中心: '作业中心',
作业分布: '作业分布',
作业名称: '作业名称',
作业名称不能为空: '作业名称不能为空',
作业状态: '作业状态',
作业编号: '作业编号',
作业趋势: '作业趋势',
保存: '保存',
修改密码: '修改密码',
修改时间: '修改时间',
@ -251,8 +249,6 @@ export default {
角色名称: '角色名称',
角色编号: '角色编号',
设置邮箱: '设置邮箱',
访问分布: '访问分布',
访问趋势: '访问趋势',
证件号码: '证件号码',
证件类型: '证件类型',
请再次输入新密码: '请再次输入新密码',
@ -470,4 +466,19 @@ export default {
: '是',
: '否',
手机: '手机',
'您已退出登录或无权限访问当前资源,请重新登录后再操作': '您已退出登录或无权限访问当前资源,请重新登录后再操作',
访问受限: '访问受限',
请求错误: '请求错误',
'正在请求不存在的服务器记录!': '正在请求不存在的服务器记录!',
'服务器发生错误!': '服务器发生错误!',
'未知错误!': '未知错误!',
'请求服务器无响应!': '请求服务器无响应!',
仪表板布局: '仪表板布局',
应用配置: '应用配置',
导出文件: '导出文件',
刷新: '刷新',
'访问分布(Today)': '访问分布(Today)',
'访问趋势(Today)': '访问趋势(Today)',
'作业分布(Today)': '作业分布(Today)',
'作业趋势(Today)': '作业趋势(Today)',
}

View File

@ -519,4 +519,8 @@ textarea {
.justify-content-center {
justify-content: center;
}
.font-monospace {
font-family: 'Lucida Console', 'Microsoft YaHei', monospace;
}

View File

@ -6,6 +6,8 @@ import router from '@/router'
import { h } from 'vue'
import jsonBigInt from 'json-bigint'
import i18 from '@/locales/index'
axios.defaults.baseURL = ''
axios.defaults.timeout = sysConfig.TIMEOUT
@ -119,64 +121,66 @@ axios.interceptors.response.use(
if (error.response) {
if (error.response.status === 404) {
ElNotification.error({
title: '请求错误',
message: 'Status:404正在请求不存在的服务器记录!',
title: i18.global.t('请求错误'),
message: i18.global.t('正在请求不存在的服务器记录!'),
})
} else if (error.response.status === 500) {
ElNotification.error({
title: '请求错误',
message: error.response.data.message || 'Status:500服务器发生错误!',
title: i18.global.t('请求错误'),
message: error.response.data.message || i18.global.t('服务器发生错误!'),
})
} else if ([401, 403].includes(error.response.status)) {
// 如果token不存在说明用户是第一次访问直接跳转到登录页面
if (!tool.cookie.get('ACCESS-TOKEN') && window.location.href.indexOf('guest') < 0) {
tool.data.set('LOGIN_REDIRECT', window.location.href)
await router.replace({ path: '/guest/login' })
return
}
if (!MessageBox_401_show && window.location.href.indexOf('guest') < 0) {
MessageBox_401_show = true
tool.timeout(100).then(async () => {
await ElMessageBox.confirm('您已退出登录或无权限访问当前资源,请重新登录后再操作', '访问受限', {
await ElMessageBox.confirm(i18.global.t('您已退出登录或无权限访问当前资源,请重新登录后再操作'), i18.global.t('访问受限'), {
type: 'error',
closeOnClickModal: false,
center: true,
confirmButtonText: '重新登录',
confirmButtonText: i18.global.t('重新登录'),
beforeClose: (action, instance, done) => {
MessageBox_401_show = false
done()
},
})
tool.data.set('LOGIN_REDIRECT', window.location.href)
await router.replace({ path: '/guest/login' })
})
}
} else if (error.response.status === 900) {
function showErr(msg) {
function showErr(key, msg) {
const title = axios.defaults.app().config.globalProperties.$GLOBAL.enums.errorCodes[error.response.data.code][1]
ElNotification.error({
title: title,
message: h('p', msg),
message: h('p', [h('b', `${key} `), h('span', msg)]),
})
}
//业务错误
if (typeof error.response.data.msg === 'object') {
Object.keys(error.response.data.msg).forEach((x) => {
showErr(error.response.data.msg[x])
showErr(x, error.response.data.msg[x])
})
} else {
showErr(error.response.data.msg)
}
} else {
ElNotification.error({
title: '请求错误',
message: error.message || `Status:${error.response.status}未知错误!`,
title: i18.global.t('请求错误'),
message: error.message || i18.global.t(`未知错误!`),
})
}
} else {
ElNotification.error({
title: '请求错误',
message: '请求服务器无响应!',
title: i18.global.t('请求错误'),
message: i18.global.t('请求服务器无响应!'),
})
}

View File

@ -72,7 +72,9 @@ export default {
this.isLoading = false
return false
}
window.location.href = '/'
const redirect = this.$TOOL.data.get('LOGIN_REDIRECT') ?? '/'
this.$TOOL.data.remove('LOGIN_REDIRECT')
window.location.href = redirect
},
},
}

View File

@ -1,21 +1,24 @@
<template>
<el-card :header="$t('作业趋势')" shadow="never" style="height: 25rem">
<el-card :header="$t('作业趋势(Today)')" shadow="never" style="height: 25rem">
<chart-bar
:api="[
{
file: 'sys_job',
name: 'getRecordBarChart',
label: '今日',
value: [tool.dateFormat(new Date(), 'yyyy-MM-dd'), tool.dateFormat(new Date(), 'yyyy-MM-dd')],
},
{
file: 'sys_job',
name: 'getRecordBarChart',
label: '昨日',
value: [
tool.dateFormat(new Date().setDate(new Date().getDate() - 1), 'yyyy-MM-dd'),
tool.dateFormat(new Date().setDate(new Date().getDate() - 1), 'yyyy-MM-dd'),
],
},
]"></chart-bar>
]"
height="20rem"></chart-bar>
</el-card>
</template>
@ -29,9 +32,9 @@ export default {
return tool
},
},
title: '作业趋势',
title: '作业趋势(Today)',
icon: 'el-icon-data-line',
description: '作业趋势',
description: '作业趋势(Today)',
components: {
ChartBar,
},

View File

@ -1,21 +1,24 @@
<template>
<el-card :header="$t('访问趋势')" shadow="never" style="height: 25rem">
<el-card :header="$t('访问趋势(Today)')" shadow="never" style="height: 25rem">
<chart-bar
:api="[
{
file: 'sys_log',
name: 'getBarChart',
label: '今日',
value: [tool.dateFormat(new Date(), 'yyyy-MM-dd'), tool.dateFormat(new Date(), 'yyyy-MM-dd')],
},
{
file: 'sys_log',
name: 'getBarChart',
label: '昨日',
value: [
tool.dateFormat(new Date().setDate(new Date().getDate() - 1), 'yyyy-MM-dd'),
tool.dateFormat(new Date().setDate(new Date().getDate() - 1), 'yyyy-MM-dd'),
],
},
]"></chart-bar>
]"
height="20rem"></chart-bar>
</el-card>
</template>
@ -29,9 +32,9 @@ export default {
return tool
},
},
title: '访问趋势',
title: '访问趋势(Today)',
icon: 'el-icon-data-line',
description: '访问趋势',
description: '访问趋势(Today)',
components: {
ChartBar,
},

View File

@ -1,5 +1,5 @@
<template>
<el-card :header="$t('作业分布')" shadow="never" style="height: 25rem">
<el-card :header="$t('作业分布(Today)')" shadow="never" style="height: 25rem">
<chart-pie
:api="[
{
@ -14,7 +14,8 @@
value: [tool.dateFormat(new Date(), 'yyyy-MM-dd'), tool.dateFormat(new Date(), 'yyyy-MM-dd')],
radius: [0, '30%'],
},
]"></chart-pie>
]"
height="20rem"></chart-pie>
</el-card>
</template>
@ -28,9 +29,9 @@ export default {
return tool
},
},
title: '作业分布',
title: '作业分布(Today)',
icon: 'el-icon-data-line',
description: '作业分布',
description: '作业分布(Today)',
components: {
ChartPie,
},

View File

@ -1,5 +1,5 @@
<template>
<el-card :header="$t('访问分布')" shadow="never" style="height: 25rem">
<el-card :header="$t('访问分布(Today)')" shadow="never" style="height: 25rem">
<chart-pie
:api="[
{
@ -14,7 +14,8 @@
value: [tool.dateFormat(new Date(), 'yyyy-MM-dd'), tool.dateFormat(new Date(), 'yyyy-MM-dd')],
radius: [0, '30%'],
},
]"></chart-pie>
]"
height="20rem"></chart-pie>
</el-card>
</template>
@ -28,9 +29,9 @@ export default {
return tool
},
},
title: '访问分布',
title: '访问分布(Today)',
icon: 'el-icon-data-line',
description: '访问分布',
description: '访问分布(Today)',
components: {
ChartPie,
},

View File

@ -1,6 +1,6 @@
<template>
<div v-loading="loading">
<scEcharts :option="option" height="20rem"></scEcharts>
<scEcharts v-bind="$attrs" :option="option"></scEcharts>
</div>
</template>
@ -46,8 +46,9 @@ export default {
this.option.xAxis.data = res[0].data.map((x) => {
return x.timestamp
})
let i = 0
this.option.series = res.map((x) => {
return { type: 'line', data: x.data.map((y) => y.value), symbol: 'none', areaStyle: {} }
return { name: this.api[i++].label, type: 'line', data: x.data.map((y) => y.value), symbol: 'none', areaStyle: {} }
})
},
mounted() {},

View File

@ -1,6 +1,6 @@
<template>
<div v-loading="loading">
<scEcharts :option="option" height="20rem"></scEcharts>
<scEcharts v-bind="$attrs" :option="option"></scEcharts>
</div>
</template>

View File

@ -8,7 +8,8 @@
</el-header>
<el-main class="nopadding">
<sc-table
:apiObj="$API.sys_api.query"
:export-api="$API.sys_api.export"
:query-api="$API.sys_api.query"
:summary-method="(x) => ['接口总数', countTotalRows(x.data)]"
default-expand-all
hidePagination

View File

@ -54,7 +54,7 @@
</el-header>
<el-main class="nopadding">
<sc-table :apiObj="$API.sys_cache.getAllEntries" :params="query" @row-click="rowClick" ref="table" row-key="key" stripe>
<sc-table :params="query" :query-api="$API.sys_cache.getAllEntries" @row-click="rowClick" ref="table" row-key="key" stripe>
<el-table-column :label="$t('键名')" prop="key" show-overflow-tooltip />
<el-table-column :label="$t('键值')" prop="data" show-overflow-tooltip />
<el-table-column :label="$t('滑动过期')" align="right" prop="sldExpTime" width="200" />

View File

@ -47,9 +47,10 @@
</el-header>
<el-main class="nopadding">
<sc-table
:apiObj="$API.sys_config.pagedQuery"
:context-menus="['id', 'userRegisterConfirm', 'enabled', 'createdTime']"
:export-api="$API.sys_config.export"
:params="query"
:query-api="$API.sys_config.pagedQuery"
:vue="this"
@selection-change="
(items) => {
@ -210,7 +211,11 @@ export default {
mounted() {
if (this.keywords) {
this.$refs.search.form.root.keywords = this.keywords
this.$refs.search.keepKeywords = this.keywords
this.$refs.search.keeps.push({
field: 'keywords',
value: this.keywords,
type: 'root',
})
}
},
props: ['keywords'],

View File

@ -10,8 +10,9 @@
<el-form-item :label="$t('默认角色')" prop="userRegisterRoleId">
<sc-select
v-model="form.userRegisterRoleId"
:apiObj="$API.sys_role.query"
:config="{ props: { label: 'name', value: 'id' } }"
:export-api="$API.sys_role.export"
:query-api="$API.sys_role.query"
clearable
filterable
style="width: 15rem" />

View File

@ -54,10 +54,11 @@
</el-header>
<el-main class="nopadding">
<sc-table
:apiObj="$API.sys_dept.query"
:context-menus="['id', 'name', 'sort', 'enabled', 'createdTime', 'summary']"
:default-sort="{ prop: 'sort', order: 'descending' }"
:export-api="$API.sys_dept.export"
:params="query"
:query-api="$API.sys_dept.query"
:vue="this"
@selection-change="
(items) => {
@ -216,7 +217,11 @@ export default {
mounted() {
if (this.keywords) {
this.$refs.search.form.root.keywords = this.keywords
this.$refs.search.keepKeywords = this.keywords
this.$refs.search.keeps.push({
field: 'keywords',
value: this.keywords,
type: 'root',
})
}
},
props: ['keywords'],

View File

@ -22,11 +22,12 @@
</el-header>
<el-main class="nopadding">
<sc-table
:apiObj="$API.sys_dic.pagedQueryContent"
:before-post="(data) => data.dynamicFilter.filters.length > 0"
:context-menus="['key', 'value', 'createdTime']"
:default-sort="{ prop: 'createdTime', order: 'descending' }"
:export-api="$API.sys_dic.exportContent"
:params="query"
:query-api="$API.sys_dic.pagedQueryContent"
:vue="this"
@selection-change="
(items) => {
@ -153,7 +154,11 @@ export default {
mounted() {
if (this.keywords) {
this.$refs.search.form.root.keywords = this.keywords
this.$refs.search.keepKeywords = this.keywords
this.$refs.search.keeps.push({
field: 'keywords',
value: this.keywords,
type: 'root',
})
}
},
props: { catalogId: Number, keywords: String },

View File

@ -31,12 +31,6 @@
<div class="left-panel">
<na-search
:controls="[
{
type: 'input',
field: ['root', 'keywords'],
placeholder: $t('作业编号 / 作业名称'),
style: 'width:20rem',
},
{
type: 'select',
field: ['dy', 'httpMethod'],
@ -46,6 +40,12 @@
placeholder: $t('请求方式'),
style: 'width:15rem',
},
{
type: 'input',
field: ['root', 'keywords'],
placeholder: $t('作业编号 / 作业名称'),
style: 'width:20rem',
},
]"
:vue="this"
@reset="Object.entries(this.$refs.selectFilter.selected).forEach(([key, _]) => (this.$refs.selectFilter.selected[key] = ['']))"
@ -73,7 +73,15 @@
</el-header>
<el-main class="nopadding">
<sc-table
:apiObj="$API.sys_job.pagedQuery"
:cell-style="
(row) => {
if (row.column.property === 'lastDuration') {
if (row.row.lastDuration > 1000) {
return { color: 'var(--el-color-danger)' }
}
}
}
"
:context-menus="[
'id',
'jobName',
@ -87,8 +95,10 @@
'createdTime',
]"
:default-sort="{ prop: 'lastExecTime', order: 'descending' }"
:export-api="$API.sys_job.export"
:page-size="100"
:params="query"
:query-api="$API.sys_job.pagedQuery"
:vue="this"
@selection-change="
(items) => {
@ -341,7 +351,11 @@ export default {
mounted() {
if (this.keywords || this.$route.query.keywords) {
this.$refs.search.form.root.keywords = this.keywords || this.$route.query.keywords
this.$refs.search.keepKeywords = this.keywords || this.$route.query.keywords
this.$refs.search.keeps.push({
field: 'keywords',
value: this.keywords || this.$route.query.keywords,
type: 'root',
})
}
},
props: ['keywords'],

View File

@ -14,7 +14,7 @@
<el-input v-model="form.id" clearable />
</el-form-item>
<el-form-item :label="$t('执行计划')" prop="executionCron">
<sc-cron v-model="form.executionCron" clearable />
<sc-cron v-model="form.executionCron" class="font-monospace" clearable />
</el-form-item>
<el-form-item :label="$t('请求方法')" prop="httpMethod">
<el-select v-model="form.httpMethod" clearable filterable>

View File

@ -20,7 +20,12 @@ const all = defineAsyncComponent(() => import('@/views/sys/job/all/index.vue'))
export default {
components: { all, fail },
computed: {},
created() {},
created() {
if (this.$route.query.view === 'fail') {
this.tabId = 'fail'
this.$TOOL.data.set('APP_SET_FAIL_JOB_VIEW_TIME', this.$TOOL.dateFormat(new Date(), 'yyyy-MM-dd hh:mm:ss'))
}
},
data() {
return {
tabId: 'all',

View File

@ -4,12 +4,6 @@
<div class="left-panel">
<na-search
:controls="[
{
type: 'input',
field: ['root', 'keywords'],
placeholder: $t('作业编号 / 执行编号'),
style: 'width:20rem',
},
{
type: 'select',
field: ['dy', 'httpMethod'],
@ -33,6 +27,12 @@
placeholder: $t('状态码'),
style: 'width:20rem',
},
{
type: 'input',
field: ['root', 'keywords'],
placeholder: $t('作业编号 / 作业名称 / 执行编号'),
style: 'width:20rem',
},
]"
:vue="this"
@search="onSearch"
@ -45,10 +45,20 @@
</el-header>
<el-main class="nopadding">
<sc-table
:apiObj="$API.sys_job.recordPagedQuery"
:cell-style="
(row) => {
if (row.column.property === 'duration') {
if (row.row.duration > 1000) {
return { color: 'var(--el-color-danger)' }
}
}
}
"
:context-menus="['id', 'duration', 'httpMethod', 'requestUrl', 'httpStatusCode', 'createdTime', 'jobId']"
:default-sort="{ prop: 'createdTime', order: 'descending' }"
:export-api="$API.sys_job.exportRecord"
:params="query"
:query-api="$API.sys_job.pagedQueryRecord"
:vue="this"
ref="table"
remote-filter
@ -90,7 +100,7 @@
<el-table-column :label="$t('作业信息')" prop="jobId" show-overflow-tooltip sortable="custom" width="500">
<template #default="{ row }">
<p>
<el-link :href="`/sys/job?keywords=${row.jobId}`" target="_blank">
<el-link @click="jobClick(row.job)">
{{ row.job.jobName }}
</el-link>
</p>
@ -111,6 +121,13 @@
@mounted="$refs.saveDialog.open(dialog.save)"
@success="(data, mode) => table.handleUpdate($refs.table, data, mode)"
ref="saveDialog"></save-dialog>
<job-dialog
v-if="dialog.job"
@closed="dialog.job = null"
@mounted="$refs.jobDialog.open(dialog.job)"
@success="(data, mode) => table.handleUpdate($refs.table, data, mode)"
ref="jobDialog"></job-dialog>
</template>
<script>
@ -120,10 +137,12 @@ import naColOperation from '@/config/naColOperation'
import naIndicator from '@/components/naIndicator/index.vue'
const saveDialog = defineAsyncComponent(() => import('./save.vue'))
const jobDialog = defineAsyncComponent(() => import('@/views/sys/job/all/save.vue'))
export default {
components: {
naIndicator,
saveDialog,
jobDialog,
},
computed: {
naColOperation() {
@ -170,6 +189,9 @@ export default {
},
inject: ['reload'],
methods: {
jobClick(job) {
this.dialog.job = { mode: 'view', row: { id: job.id } }
},
//搜索
onSearch(form) {
if (Array.isArray(form.dy.createdTime)) {
@ -216,16 +238,29 @@ export default {
mounted() {
if (this.keywords) {
this.$refs.search.form.root.keywords = this.keywords
this.$refs.search.keepKeywords = this.keywords
this.$refs.search.keeps.push({
field: 'keywords',
value: this.keywords,
type: 'root',
})
}
if (this.statusCodes) {
this.$refs.search.form.dy.httpStatusCode = this.statusCodes
this.$refs.search.keepHttpStatusCode = this.statusCodes
this.$refs.search.keeps.push({
field: 'httpStatusCode',
value: this.statusCodes,
type: 'dy',
})
}
this.$refs.search.form.dy.createdTime = this.$refs.search.keepCreatedTime = [
this.$refs.search.form.dy.createdTime = [
`${this.$TOOL.dateFormat(new Date(), 'yyyy-MM-dd')} 00:00:00`,
`${this.$TOOL.dateFormat(new Date(), 'yyyy-MM-dd')} 00:00:00`,
]
this.$refs.search.keeps.push({
field: 'createdTime',
value: this.$refs.search.form.dy.createdTime,
type: 'dy',
})
},
props: ['keywords', 'statusCodes'],
watch: {},

View File

@ -40,11 +40,12 @@
</el-header>
<el-main class="nopadding">
<sc-table
:apiObj="$API.sys_log.pagedQuery"
:context-menus="['id', 'httpStatusCode', 'createdClientIp', 'createdUserAgent', 'createdTime']"
:context-opers="['view']"
:default-sort="{ prop: 'createdTime', order: 'descending' }"
:export-api="$API.sys_log.export"
:params="query"
:query-api="$API.sys_log.pagedQuery"
:vue="this"
@row-click="rowClick"
ref="table"
@ -167,13 +168,22 @@ export default {
mounted() {
if (this.keywords) {
this.$refs.search.form.root.keywords = this.keywords
this.$refs.search.keepKeywords = this.keywords
this.$refs.search.keeps.push({
field: 'keywords',
value: this.keywords,
type: 'root',
})
}
this.$refs.search.form.dy.apiId = 'api/sys/user/login.by.pwd'
this.$refs.search.form.dy.createdTime = this.$refs.search.keepCreatedTime = [
this.$refs.search.form.dy.createdTime = [
`${this.$TOOL.dateFormat(new Date(), 'yyyy-MM-dd')} 00:00:00`,
`${this.$TOOL.dateFormat(new Date(), 'yyyy-MM-dd')} 00:00:00`,
]
this.$refs.search.keeps.push({
field: 'createdTime',
value: this.$refs.search.form.dy.createdTime,
type: 'dy',
})
},
props: ['keywords'],
watch: {},

View File

@ -62,11 +62,12 @@
</el-header>
<el-main class="nopadding">
<sc-table
:apiObj="$API.sys_log.pagedQuery"
:context-menus="['id', 'httpStatusCode', 'apiId', 'userId', 'method', 'duration', 'createdClientIp', 'createdTime']"
:context-opers="[]"
:default-sort="{ prop: 'createdTime', order: 'descending' }"
:export-api="$API.sys_log.export"
:params="query"
:query-api="$API.sys_log.pagedQuery"
:vue="this"
ref="table"
remote-filter
@ -146,6 +147,9 @@ export default {
if (this.keywords) {
this.query.keywords = this.keywords
}
if (this.userId) {
this.query.dynamicFilter.filters.push({ field: 'userId', operator: 'eq', value: this.userId })
}
},
data() {
return {
@ -200,6 +204,15 @@ export default {
value: form.dy.apiId,
})
}
if (typeof form.dy.userId) {
this.query.dynamicFilter.filters.push({
field: 'userId',
operator: 'eq',
value: form.dy.userId,
})
}
if (typeof form.dy.operationResult === 'boolean') {
this.query.dynamicFilter.filters.push(
form.dy.operationResult
@ -230,14 +243,32 @@ export default {
mounted() {
if (this.keywords) {
this.$refs.search.form.root.keywords = this.keywords
this.$refs.search.keepKeywords = this.keywords
this.$refs.search.keeps.push({
field: 'keywords',
value: this.keywords,
type: 'root',
})
}
this.$refs.search.form.dy.createdTime = this.$refs.search.keepCreatedTime = [
if (this.userId) {
this.$refs.search.keeps.push({
field: 'userId',
value: this.userId,
type: 'dy',
})
}
this.$refs.search.form.dy.createdTime = [
`${this.$TOOL.dateFormat(new Date(), 'yyyy-MM-dd')} 00:00:00`,
`${this.$TOOL.dateFormat(new Date(), 'yyyy-MM-dd')} 00:00:00`,
]
this.$refs.search.keeps.push({
field: 'createdTime',
value: this.$refs.search.form.dy.createdTime,
type: 'dy',
})
},
props: ['keywords'],
props: ['keywords', 'userId'],
watch: {},
}
</script>

View File

@ -41,10 +41,11 @@
</el-header>
<el-main class="nopadding">
<sc-table
:apiObj="$API.sys_sitemsg.pagedQuery"
:context-menus="['id', 'createdUserName', 'msgType', 'title', 'summary', 'createdTime']"
:default-sort="{ prop: 'createdTime', order: 'descending' }"
:export-api="$API.sys_sitemsg.export"
:params="query"
:query-api="$API.sys_sitemsg.pagedQuery"
:vue="this"
@selection-change="
(items) => {
@ -172,7 +173,11 @@ export default {
mounted() {
if (this.keywords) {
this.$refs.search.form.root.keywords = this.keywords
this.$refs.search.keepKeywords = this.keywords
this.$refs.search.keeps.push({
field: 'keywords',
value: this.keywords,
type: 'root',
})
}
},
props: ['keywords'],

View File

@ -32,8 +32,8 @@
<sc-select
v-if="!this.loading"
v-model="form.roleIds"
:apiObj="$API.sys_role.query"
:config="{ props: { label: 'name', value: 'id' } }"
:query-api="$API.sys_role.query"
class="w100p"
clearable
filterable

View File

@ -72,10 +72,11 @@
</el-header>
<el-main class="nopadding">
<sc-table
:apiObj="$API.sys_role.pagedQuery"
:context-menus="['id', 'name', 'sort', 'enabled', 'ignorePermissionControl', 'dataScope', 'displayDashboard', 'createdTime']"
:default-sort="{ prop: 'sort', order: 'descending' }"
:export-api="$API.sys_role.export"
:params="query"
:query-api="$API.sys_role.pagedQuery"
:vue="this"
@selection-change="
(items) => {
@ -301,7 +302,11 @@ export default {
mounted() {
if (this.keywords) {
this.$refs.search.form.root.keywords = this.keywords
this.$refs.search.keepKeywords = this.keywords
this.$refs.search.keeps.push({
field: 'keywords',
value: this.keywords,
type: 'root',
})
}
},
props: ['keywords'],

View File

@ -74,6 +74,14 @@
</el-select>
<div class="el-form-item-msg">{{ $t('用于控制角色登录后控制台的视图') }}</div>
</el-form-item>
<el-form-item v-if="form.displayDashboard" :label="$t('仪表板布局')">
<v-ace-editor
v-model:value="form.dashboardLayout"
:theme="this.$TOOL.data.get('APP_SET_DARK') || this.$CONFIG.APP_SET_DARK ? 'github_dark' : 'github'"
lang="json"
style="height: 30rem; width: 100%" />
<el-button @click="form.dashboardLayout = jsonFormat(form.dashboardLayout)" type="text">{{ $t('JSON格式化') }}</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane v-if="mode === 'view'" :label="$t('用户列表')" name="user">
@ -99,14 +107,17 @@
<script>
import { defineAsyncComponent } from 'vue'
import vkbeautify from 'vkbeautify/index'
import config from '@/config/index'
const User = defineAsyncComponent(() => import('@/views/sys/user/index.vue'))
export default {
components: { User },
created() {},
data() {
return {
//表单数据
form: { displayDashboard: false, sort: 100, enabled: true },
form: { displayDashboard: false, dashboardLayout: this.jsonFormat(config.APP_SET_HOME_GRID), sort: 100, enabled: true },
loading: false,
mode: 'add',
//验证规则
@ -136,6 +147,14 @@ export default {
},
emits: ['success', 'closed', 'mounted'],
methods: {
jsonFormat(obj) {
try {
obj = vkbeautify.json(obj, 2)
} catch {
this.$message.error(this.$t('非JSON格式'))
}
return obj
},
async getTrees(name) {
const res = await this.$API[`sys_${name}`].query.post()
this.trees[name] = res.data

View File

@ -69,11 +69,12 @@
</el-header>
<el-main class="nopadding">
<sc-table
:apiObj="$API.sys_user.pagedQuery"
:context-menus="['id', 'userName', 'mobile', 'email', 'enabled', 'createdTime']"
:context-opers="['view', 'edit']"
:default-sort="{ prop: 'createdTime', order: 'descending' }"
:export-api="$API.sys_user.export"
:params="query"
:query-api="$API.sys_user.pagedQuery"
:vue="this"
@selection-change="
(items) => {
@ -238,7 +239,11 @@ export default {
mounted() {
if (this.keywords) {
this.$refs.search.form.root.keywords = this.keywords
this.$refs.search.keepKeywords = this.keywords
this.$refs.search.keeps.push({
field: 'keywords',
value: this.keywords,
type: 'root',
})
}
},
props: ['keywords', 'roleId', 'deptId'],

View File

@ -8,13 +8,13 @@
full-screen>
<el-form
v-loading="loading"
:disabled="mode === 'view'"
:disabled="mode === 'view' && tabId !== 'log'"
:model="form"
:rules="rules"
label-position="right"
label-width="12rem"
ref="dialogForm">
<el-tabs tab-position="top">
<el-tabs v-model="tabId" tab-position="top">
<el-tab-pane :label="$t('基本信息')">
<el-form-item prop="avatar">
<sc-upload v-model="form.avatar" :title="$t('上传头像')"></sc-upload>
@ -54,8 +54,8 @@
<sc-select
v-if="!this.loading"
v-model="form.roleIds"
:apiObj="$API.sys_role.query"
:config="{ props: { label: 'name', value: 'id' } }"
:query-api="$API.sys_role.query"
class="w100p"
clearable
filterable
@ -220,8 +220,20 @@
</el-input>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item :label="$t('应用配置')" prop="profile.appConfig">
<v-ace-editor
v-model:value="form.profile.appConfig"
:theme="this.$TOOL.data.get('APP_SET_DARK') || this.$CONFIG.APP_SET_DARK ? 'github_dark' : 'github'"
lang="json"
style="height: 10rem; width: 100%" />
</el-form-item>
</el-col>
</el-row>
</el-tab-pane>
<el-tab-pane v-if="mode === 'view'" :label="$t('操作日志')" name="log">
<log v-if="tabId === 'log'" :user-id="form.id"></log>
</el-tab-pane>
<el-tab-pane v-if="mode === 'view'" :label="$t('原始数据')">
<json-viewer
:expand-depth="5"
@ -241,13 +253,17 @@
</template>
<script>
import { defineAsyncComponent } from 'vue'
const log = defineAsyncComponent(() => import('@/views/sys/log/operation/index.vue'))
export default {
components: {},
components: { log },
data() {
return {
//表单数据
form: {
profile: {
appConfig: '[]',
nationArea: '',
homeArea: '',
companyArea: '',
@ -293,6 +309,7 @@ export default {
},
],
},
tabId: '0',
titleMap: {
add: this.$t('新增用户'),
edit: this.$t('编辑用户'),