mirror of
https://github.com/jumpserver/lina.git
synced 2026-01-29 21:28:52 +00:00
feat: 审批模版管理 (#964)
Co-authored-by: feng626 <1304903146@qq.com> Co-authored-by: Jiangjie.Bai <32935519+BaiJiangJie@users.noreply.github.com>
This commit is contained in:
@@ -23,5 +23,5 @@ VUE_APP_LOGOUT_PATH = '/core/auth/logout/'
|
||||
|
||||
# Dev server for core proxy
|
||||
VUE_APP_CORE_HOST = 'http://localhost:8080'
|
||||
VUE_APP_CORE_WS = 'ws://localhost:8070'
|
||||
VUE_APP_CORE_WS = 'ws://localhost:8080'
|
||||
VUE_APP_ENV = 'development'
|
||||
|
||||
@@ -230,6 +230,8 @@
|
||||
"ReLogin": "重新登录"
|
||||
},
|
||||
"common": {
|
||||
"DateUpdated": "更新日期",
|
||||
"ApprovaLevel": "审批信息",
|
||||
"MFAVerify": "验证 MFA",
|
||||
"ViewSecret": "查看密码",
|
||||
"ConnectWebSocketError": "连接 WebSocket 失败",
|
||||
@@ -547,6 +549,9 @@
|
||||
},
|
||||
"route": {
|
||||
"": "",
|
||||
"TicketFlow": "工单流",
|
||||
"TicketFlowCreate": "创建审批流",
|
||||
"TicketFlowUpdate": "更新审批流",
|
||||
"Accounts": "账号管理",
|
||||
"AssetAccount": "资产账号",
|
||||
"ApplicationAccount": "应用账号",
|
||||
@@ -661,6 +666,10 @@
|
||||
"TicketDetail": "工单详情",
|
||||
"TicketCreate": "创建工单",
|
||||
"Tickets": "工单管理",
|
||||
"Templates": "模版管理",
|
||||
"TemplateDetail": "模版详情",
|
||||
"TemplateCreate": "创建模版",
|
||||
"TemplateUpdate": "更新模版",
|
||||
"UserCreate": "创建用户",
|
||||
"UserDetail": "用户详情",
|
||||
"UserFirstLogin": "首次登录",
|
||||
@@ -899,6 +908,12 @@
|
||||
"setting": "设置"
|
||||
},
|
||||
"tickets": {
|
||||
"OneAssigneeType": "一级受理人类型",
|
||||
"OneAssignee": "一级受理人",
|
||||
"TwoAssigneeType": "二级受理人类型",
|
||||
"TwoAssignee": "二级受理人",
|
||||
"ApprovalLevel": "审批级别",
|
||||
"FlowDetail": "流程详情",
|
||||
"PermissionName": "授权规则名称",
|
||||
"Accept": "同意",
|
||||
"AssignedMe": "待我审批",
|
||||
@@ -916,7 +931,6 @@
|
||||
"status": "状态",
|
||||
"title": "标题",
|
||||
"action": "动作",
|
||||
"IPGroup": "IP 组",
|
||||
"type": "类型",
|
||||
"user": "用户",
|
||||
"Status": "状态",
|
||||
@@ -1183,6 +1197,9 @@
|
||||
"Log": "日志",
|
||||
"DeleteReleasedAssets": "删除已释放资产"
|
||||
},
|
||||
"Template": {
|
||||
"Template": "模版管理"
|
||||
},
|
||||
"Corporation": "公司",
|
||||
"Edition": "版本",
|
||||
"Execute": "执行",
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
<template v-for="item in submenu">
|
||||
<el-tab-pane :key="item.name" :label-content="item.labelContent" :name="item.name" :disabled="item.disabled">
|
||||
<span slot="label">
|
||||
<i v-if="item.icon" class="fa " :class="item.icon" />
|
||||
{{ item.title }}
|
||||
<slot name="badge" :tab="item.name" />
|
||||
</span>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import i18n from '@/i18n/i18n'
|
||||
import empty from '@/layout/empty'
|
||||
export default [
|
||||
{
|
||||
path: 'tickets',
|
||||
@@ -55,5 +56,35 @@ export default [
|
||||
component: () => import('@/views/tickets/CommandConfirm/Detail/index'),
|
||||
meta: { title: i18n.t('route.CommandConfirm'), activeMenu: '/tickets/tickets' },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'flows',
|
||||
name: 'TicketFlowList',
|
||||
component: empty,
|
||||
meta: { title: i18n.t('route.TicketFlow'), icon: 'check-square-o', activeMenu: '/tickets/tickets' },
|
||||
hidden: true,
|
||||
children: [
|
||||
{
|
||||
path: 'create',
|
||||
name: 'TicketFlowCreate',
|
||||
component: () => import('@/views/tickets/TicketFlow/FlowCreateUpdate'),
|
||||
meta: { title: i18n.t('route.TicketFlowCreate') },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: ':id/update',
|
||||
name: 'TicketFlowUpdate',
|
||||
component: () => import('@/views/tickets/TicketFlow/FlowCreateUpdate'),
|
||||
meta: { title: i18n.t('route.TicketFlowUpdate') },
|
||||
hidden: true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'tickets/flow/:id',
|
||||
name: 'FlowDetail',
|
||||
component: () => import('@/views/tickets/TicketFlow/Detail/index'),
|
||||
meta: { title: i18n.t('route.TicketFlow'), activeMenu: '/tickets/tickets' },
|
||||
hidden: true
|
||||
}
|
||||
]
|
||||
|
||||
115
src/views/tickets/TicketFlow/Detail/TicketDetail.vue
Normal file
115
src/views/tickets/TicketFlow/Detail/TicketDetail.vue
Normal file
@@ -0,0 +1,115 @@
|
||||
<template>
|
||||
<GenericTicketDetail
|
||||
:object="object"
|
||||
:detail-card-items="detailCardItems"
|
||||
:special-card-items="specialCardItems"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { formatTime, getDateTimeStamp } from '@/utils/index'
|
||||
import { toSafeLocalDateStr } from '@/utils/common'
|
||||
import GenericTicketDetail from '@/views/tickets/TicketFlow/components/GenericTicketDetail'
|
||||
export default {
|
||||
name: '',
|
||||
components: { GenericTicketDetail },
|
||||
props: {
|
||||
object: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
comments: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
detailCardItems() {
|
||||
return [
|
||||
{
|
||||
key: this.$t('tickets.type'),
|
||||
value: this.object.type_display
|
||||
},
|
||||
{
|
||||
key: this.$t('tickets.ApprovalLevel'),
|
||||
value: this.object.approval_level + '级'
|
||||
},
|
||||
{
|
||||
key: this.$t('common.CreatedBy'),
|
||||
value: this.object.created_by
|
||||
},
|
||||
{
|
||||
key: this.$t('common.dateCreated'),
|
||||
value: toSafeLocalDateStr(this.object.date_created)
|
||||
},
|
||||
{
|
||||
key: this.$t('common.DateUpdated'),
|
||||
value: toSafeLocalDateStr(this.object.date_updated)
|
||||
}
|
||||
]
|
||||
},
|
||||
specialCardItems() {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const approvalData = [
|
||||
{
|
||||
key: this.$t('tickets.OneAssigneeType'),
|
||||
value: ''
|
||||
},
|
||||
{
|
||||
key: this.$t('tickets.OneAssignee'),
|
||||
value: ''
|
||||
},
|
||||
{
|
||||
key: this.$t('tickets.TwoAssigneeType'),
|
||||
value: ''
|
||||
},
|
||||
{
|
||||
key: this.$t('tickets.TwoAssignee'),
|
||||
value: ''
|
||||
}]
|
||||
this.object.rules.forEach((item, index) => {
|
||||
console.log(item, index)
|
||||
if (index === 0) {
|
||||
approvalData[0].value = item.strategy_display
|
||||
approvalData[1].value = item.assignees_display.join(',')
|
||||
} else {
|
||||
approvalData[2].value = item.strategy_display
|
||||
approvalData[3].value = item.assignees_display.join(',')
|
||||
}
|
||||
})
|
||||
return approvalData.slice(0, this.object.rules.length * 2)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
formatTime(dateStr) {
|
||||
return formatTime(getDateTimeStamp(dateStr))
|
||||
},
|
||||
toSafeLocalDateStr(dataStr) {
|
||||
return toSafeLocalDateStr(dataStr)
|
||||
},
|
||||
reloadPage() {
|
||||
window.location.reload()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.assets{
|
||||
margin-top: 14px;
|
||||
}
|
||||
.feed-activity-list .feed-element {
|
||||
border-bottom: 1px solid #e7eaec;
|
||||
}
|
||||
.feed-element > .pull-left {
|
||||
margin-right: 10px;
|
||||
}
|
||||
.feed-element .header-avatar {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
}
|
||||
.box {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
</style>
|
||||
49
src/views/tickets/TicketFlow/Detail/index.vue
Normal file
49
src/views/tickets/TicketFlow/Detail/index.vue
Normal file
@@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<GenericDetailPage :object.sync="ticket" :active-menu.sync="config.activeMenu" v-bind="config" v-on="$listeners">
|
||||
<component :is="config.activeMenu" :object="ticket" />
|
||||
</GenericDetailPage>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { GenericDetailPage, TabPage } from '@/layout/components'
|
||||
import TicketDetail from './TicketDetail'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GenericDetailPage,
|
||||
TicketDetail,
|
||||
TabPage
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
ticket: { user_display: '', type_display: '', status: '', assignees_display: '', date_created: '' },
|
||||
config: {
|
||||
activeMenu: 'TicketDetail',
|
||||
url: '',
|
||||
submenu: [
|
||||
{
|
||||
title: this.$t('route.TicketDetail'),
|
||||
name: 'TicketDetail'
|
||||
}
|
||||
],
|
||||
actions: {
|
||||
detailApiUrl: `/api/v1/tickets/flows/${this.$route.params.id}/`
|
||||
},
|
||||
getObjectName: this.getObjectName,
|
||||
hasRightSide: false
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
},
|
||||
methods: {
|
||||
getObjectName() {
|
||||
return this.ticket.id
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
60
src/views/tickets/TicketFlow/FlowCreateUpdate.vue
Normal file
60
src/views/tickets/TicketFlow/FlowCreateUpdate.vue
Normal file
@@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<GenericCreateUpdatePage :initial="initial" v-bind="$data" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { GenericCreateUpdatePage } from '@/layout/components'
|
||||
import FlowRuleField from './FlowRuleField'
|
||||
export default {
|
||||
name: 'FlowCreateUpdate',
|
||||
components: {
|
||||
GenericCreateUpdatePage
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
fields: [
|
||||
[this.$t('common.Basic'), ['type']],
|
||||
[this.$t('common.ApprovaLevel'), ['approval_level', 'rules']]
|
||||
],
|
||||
fieldsMeta: {
|
||||
rules: {
|
||||
label: '审批流程',
|
||||
component: FlowRuleField,
|
||||
el: {
|
||||
level: 1
|
||||
},
|
||||
hidden: (form) => {
|
||||
this.fieldsMeta.rules.el.level = form['approval_level']
|
||||
}
|
||||
}
|
||||
},
|
||||
getUrl() {
|
||||
const params = this.$route.params
|
||||
let url = `/api/v1/tickets/flows/`
|
||||
if (params.id) {
|
||||
url = `${url}${params.id}/`
|
||||
}
|
||||
return `${url}`
|
||||
},
|
||||
updateSuccessNextRoute: { name: 'TicketList' },
|
||||
createSuccessNextRoute: { name: 'TicketList' }
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
initial() {
|
||||
return this.$route.query
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.$store.state.users.profile.user_all_orgs.length > 0) {
|
||||
this.initial.org_id = this.$store.state.users.profile.user_all_orgs[0].id
|
||||
}
|
||||
this.loading = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
</style>
|
||||
97
src/views/tickets/TicketFlow/FlowRuleField.vue
Normal file
97
src/views/tickets/TicketFlow/FlowRuleField.vue
Normal file
@@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<!-- 自定义组件 my-input -->
|
||||
<div>
|
||||
<div v-for="(item, i) of data.slice(0, level)" :key="i" style="margin-bottom: 10px">
|
||||
<el-card class="box-card">
|
||||
<div slot="header" class="clearfix">
|
||||
<span>{{ i + 1 + '级审批' }}</span>
|
||||
</div>
|
||||
<el-radio-group v-model="item.strategy" @change="onChange()">
|
||||
<el-radio label="super">超级管理员</el-radio>
|
||||
<el-radio label="admin">组织用户</el-radio>
|
||||
<el-radio label="super_admin">超级管理员+组织用户</el-radio>
|
||||
<el-radio label="custom">自定义</el-radio>
|
||||
</el-radio-group>
|
||||
<br>
|
||||
<Select2 v-show="item.strategy === 'custom'" v-model="item.assignees" v-bind="select2Option" @change="onChange()" />
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Select2 from '@/components/FormFields/Select2'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Select2
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
type: [String, Array],
|
||||
default: () => []
|
||||
},
|
||||
level: {
|
||||
type: Number,
|
||||
default: 1
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
vm: this,
|
||||
data: [],
|
||||
initData: [
|
||||
{
|
||||
strategy: 'super',
|
||||
assignees_read_only: []
|
||||
},
|
||||
{
|
||||
strategy: 'super',
|
||||
assignees_read_only: []
|
||||
}
|
||||
],
|
||||
select2Option: {
|
||||
url: '/api/v1/users/users/'
|
||||
},
|
||||
fields: [
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
||||
},
|
||||
watch: {
|
||||
level(value) {
|
||||
console.log('Value is: ', value)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.data = this.value.concat(this.initData)
|
||||
this.data.forEach(item => {
|
||||
item.assignees = item.assignees_read_only
|
||||
delete item.assignees_read_only
|
||||
})
|
||||
this.$emit('input', this.data.slice(0, this.level))
|
||||
},
|
||||
methods: {
|
||||
onChange() {
|
||||
this.$emit('input', this.data.slice(0, this.level))
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.text {
|
||||
font-size: 14px;
|
||||
font-family: 'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif;
|
||||
}
|
||||
|
||||
.item {
|
||||
padding: 18px 0;
|
||||
}
|
||||
|
||||
.box-card {
|
||||
width: 600px;
|
||||
}
|
||||
</style>
|
||||
62
src/views/tickets/TicketFlow/TicketFlow.vue
Normal file
62
src/views/tickets/TicketFlow/TicketFlow.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<GenericListTable ref="GenericListTable" :table-config="tableConfig" :header-actions="headerActions" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { GenericListTable } from '@/layout/components'
|
||||
import { DetailFormatter } from '@/components/TableFormatters'
|
||||
export default {
|
||||
name: 'TicketFlow',
|
||||
components: {
|
||||
GenericListTable
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
return {
|
||||
tableConfig: {
|
||||
url: '/api/v1/tickets/flows/',
|
||||
columns: [
|
||||
'type_display', 'created_by',
|
||||
'date_created', 'date_updated', 'actions'
|
||||
],
|
||||
columnsShow: {
|
||||
min: ['actions'],
|
||||
default: [
|
||||
'type_display', 'created_by',
|
||||
'date_created', 'date_updated', 'actions'
|
||||
]
|
||||
},
|
||||
columnsMeta: {
|
||||
type_display: {
|
||||
formatter: DetailFormatter,
|
||||
formatterArgs: {
|
||||
route: 'FlowDetail'
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
prop: 'actions',
|
||||
formatterArgs: {
|
||||
hasClone: false,
|
||||
hasDelete: false,
|
||||
onClone: ({ row }) => {
|
||||
vm.$router.push({ name: 'TicketFlowUpdate', query: { type: row.type, clone_from: row.id }})
|
||||
},
|
||||
onUpdate: ({ row }) => {
|
||||
vm.$router.push({ name: 'TicketFlowUpdate', params: { id: row.id }})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
hasLeftActions: false,
|
||||
hasRightActions: false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
77
src/views/tickets/TicketFlow/components/Details.vue
Normal file
77
src/views/tickets/TicketFlow/components/Details.vue
Normal file
@@ -0,0 +1,77 @@
|
||||
<template>
|
||||
<IBox class="box">
|
||||
<div slot="header" class="clearfix ibox-title">
|
||||
<i class="fa fa-info-circle" /> {{ title }}
|
||||
</div>
|
||||
<div class="content">
|
||||
<el-row :gutter="10">
|
||||
<el-col v-for="item in detailCardItems" :key="'card-' + item.key" :span="12">
|
||||
<el-row class="item">
|
||||
<el-col :span="6">
|
||||
<div :style="{ 'text-align': 'align' }" class="item-label">
|
||||
<label>{{ item.key }}: </label>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="18">
|
||||
<div class="item-text">
|
||||
<ItemValue v-bind="item" />
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-divider v-if="specialCardItems.length > 0" />
|
||||
<el-row :gutter="10">
|
||||
<el-col v-for="item in specialCardItems" :key="'card-' + item.key" :span="24">
|
||||
<el-row class="item">
|
||||
<el-col :span="6">
|
||||
<div :style="{ 'text-align': 'align' }" class="item-label">
|
||||
<label>{{ item.key }}: </label>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="18">
|
||||
<div class="item-text">
|
||||
<ItemValue v-bind="item" />
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</IBox>
|
||||
</template>
|
||||
<script>
|
||||
import ItemValue from '@/components/DetailCard/ItemValue'
|
||||
import IBox from '@/components/IBox'
|
||||
export default {
|
||||
name: 'Details',
|
||||
components: { ItemValue, IBox },
|
||||
props: {
|
||||
specialCardItems: {
|
||||
type: Array,
|
||||
default: () => ([])
|
||||
},
|
||||
detailCardItems: {
|
||||
type: Array,
|
||||
default: () => ([])
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped>
|
||||
.box {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.content {
|
||||
font-size: 13px;
|
||||
line-height: 2.5;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<el-row>
|
||||
<el-col :span="17">
|
||||
<Details :detail-card-items="detailCardItems" :title="$t('common.BasicInfo')" />
|
||||
<Details v-if="specialCardItems.length > 0" :detail-card-items="specialCardItems" :title="$t('common.ApprovaLevel')" />
|
||||
<slot id="MoreDetails" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Details from './Details'
|
||||
export default {
|
||||
name: 'GenericTicketDetail',
|
||||
components: { Details },
|
||||
props: {
|
||||
object: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
specialCardItems: {
|
||||
type: Array,
|
||||
default: () => ([])
|
||||
},
|
||||
detailCardItems: {
|
||||
type: Array,
|
||||
default: () => ([])
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,6 +1,13 @@
|
||||
<template>
|
||||
<TabPage :active-menu.sync="config.activeMenu" :submenu="config.submenu">
|
||||
<el-badge v-if="props.tab === 'AssignedTicketList'" slot="badge" slot-scope="props" :value="getBadgeValue(props)" size="mini" type="primary" />
|
||||
<el-badge
|
||||
v-if="props.tab === 'AssignedTicketList'"
|
||||
slot="badge"
|
||||
slot-scope="props"
|
||||
:value="getBadgeValue(props)"
|
||||
size="mini"
|
||||
type="primary"
|
||||
/>
|
||||
<keep-alive>
|
||||
<component :is="config.activeMenu" />
|
||||
</keep-alive>
|
||||
@@ -10,16 +17,18 @@
|
||||
<script>
|
||||
import { TabPage } from '@/layout/components'
|
||||
import { mapGetters } from 'vuex'
|
||||
import { getTicketOpenCount } from '@/api/ticket'
|
||||
import AssignedTicketList from './AssignedTicketList'
|
||||
import MyTicketList from './MyTicketList'
|
||||
import { getTicketOpenCount } from '@/api/ticket'
|
||||
import TicketFlow from './TicketFlow/TicketFlow'
|
||||
|
||||
export default {
|
||||
name: 'Index',
|
||||
components: {
|
||||
TabPage,
|
||||
AssignedTicketList,
|
||||
MyTicketList
|
||||
MyTicketList,
|
||||
TicketFlow
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -34,6 +43,11 @@ export default {
|
||||
{
|
||||
title: this.$t('tickets.AssignedMe'),
|
||||
name: 'AssignedTicketList'
|
||||
},
|
||||
{
|
||||
title: '流程设置',
|
||||
icon: 'fa-gear',
|
||||
name: 'TicketFlow'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user