Commit d79b23c8a242432990be431a8c43633bd212c47d

Authored by xingyu4j
1 parent 1c1cafdc

feat: add modal component

yudao-ui-admin-vue3/src/components/XModal/index.ts 0 → 100644
  1 +import XModal from './src/XModal.vue'
  2 +
  3 +export { XModal }
yudao-ui-admin-vue3/src/components/XModal/src/XModal.vue 0 → 100644
  1 +<script setup lang="ts">
  2 +import { propTypes } from '@/utils/propTypes'
  3 +import { computed, useAttrs, useSlots } from 'vue'
  4 +
  5 +const slots = useSlots()
  6 +
  7 +const props = defineProps({
  8 + id: propTypes.string.def('model_1'),
  9 + modelValue: propTypes.bool.def(false),
  10 + fullscreen: propTypes.bool.def(false),
  11 + loading: propTypes.bool.def(false),
  12 + title: propTypes.string.def('弹窗'),
  13 + width: propTypes.string.def('800'),
  14 + height: propTypes.string.def('480'),
  15 + minWidth: propTypes.string.def('460'),
  16 + minHeight: propTypes.string.def('320'),
  17 + showFooter: propTypes.bool.def(true)
  18 +})
  19 +
  20 +const getBindValue = computed(() => {
  21 + const delArr: string[] = ['title']
  22 + const attrs = useAttrs()
  23 + const obj = { ...attrs, ...props }
  24 + for (const key in obj) {
  25 + if (delArr.indexOf(key) !== -1) {
  26 + delete obj[key]
  27 + }
  28 + }
  29 + return obj
  30 +})
  31 +</script>
  32 +
  33 +<template>
  34 + <vxe-modal
  35 + v-bind="getBindValue"
  36 + :width="width"
  37 + :height="height"
  38 + :title="title"
  39 + min-width="460"
  40 + min-height="320"
  41 + :loading="loading"
  42 + :fullscreen="fullscreen"
  43 + destroy-on-close
  44 + show-zoom
  45 + resize
  46 + transfer
  47 + :show-footer="showFooter"
  48 + >
  49 + <template v-if="slots.header" #header>
  50 + <slot name="header"></slot>
  51 + </template>
  52 + <template v-if="slots.default" #default>
  53 + <slot name="default"></slot>
  54 + </template>
  55 + <template v-if="slots.corner" #corner>
  56 + <slot name="corner"></slot>
  57 + </template>
  58 + <template v-if="slots.footer" #footer>
  59 + <slot name="footer"></slot>
  60 + </template>
  61 + </vxe-modal>
  62 +</template>
yudao-ui-admin-vue3/src/components/index.ts
@@ -4,6 +4,7 @@ import { Form } from &#39;@/components/Form&#39; @@ -4,6 +4,7 @@ import { Form } from &#39;@/components/Form&#39;
4 import { Table } from '@/components/Table' 4 import { Table } from '@/components/Table'
5 import { Search } from '@/components/Search' 5 import { Search } from '@/components/Search'
6 import { Dialog } from '@/components/Dialog' 6 import { Dialog } from '@/components/Dialog'
  7 +import { XModal } from '@/components/XModal'
7 import { DictTag } from '@/components/DictTag' 8 import { DictTag } from '@/components/DictTag'
8 import { ContentWrap } from '@/components/ContentWrap' 9 import { ContentWrap } from '@/components/ContentWrap'
9 import { Descriptions } from '@/components/Descriptions' 10 import { Descriptions } from '@/components/Descriptions'
@@ -14,6 +15,7 @@ export const setupGlobCom = (app: App&lt;Element&gt;): void =&gt; { @@ -14,6 +15,7 @@ export const setupGlobCom = (app: App&lt;Element&gt;): void =&gt; {
14 app.component('Table', Table) 15 app.component('Table', Table)
15 app.component('Search', Search) 16 app.component('Search', Search)
16 app.component('Dialog', Dialog) 17 app.component('Dialog', Dialog)
  18 + app.component('XModal', XModal)
17 app.component('DictTag', DictTag) 19 app.component('DictTag', DictTag)
18 app.component('ContentWrap', ContentWrap) 20 app.component('ContentWrap', ContentWrap)
19 app.component('Descriptions', Descriptions) 21 app.component('Descriptions', Descriptions)
yudao-ui-admin-vue3/src/hooks/web/useVxeGrid.ts
@@ -4,6 +4,7 @@ import { VxeGridProps } from &#39;vxe-table&#39; @@ -4,6 +4,7 @@ import { VxeGridProps } from &#39;vxe-table&#39;
4 export const useVxeGrid = (allSchemas, getPageApi) => { 4 export const useVxeGrid = (allSchemas, getPageApi) => {
5 const gridOptions = reactive<VxeGridProps>({ 5 const gridOptions = reactive<VxeGridProps>({
6 loading: false, 6 loading: false,
  7 + height: 800,
7 rowConfig: { 8 rowConfig: {
8 keyField: 'id', 9 keyField: 'id',
9 isHover: true 10 isHover: true
yudao-ui-admin-vue3/src/plugins/vxeTable/index.ts
@@ -114,8 +114,10 @@ VXETable.setup({ @@ -114,8 +114,10 @@ VXETable.setup({
114 titleColon: true // 是否显示标题冒号 114 titleColon: true // 是否显示标题冒号
115 }, 115 },
116 modal: { 116 modal: {
117 - width: 600, // 窗口的宽度  
118 - height: 400, // 窗口的高度 117 + width: 800, // 窗口的宽度
  118 + height: 600, // 窗口的高度
  119 + minWidth: 460,
  120 + minHeight: 320,
119 showZoom: true, // 标题是否标显示最大化与还原按钮 121 showZoom: true, // 标题是否标显示最大化与还原按钮
120 resize: true, // 是否允许窗口边缘拖动调整窗口大小 122 resize: true, // 是否允许窗口边缘拖动调整窗口大小
121 marginSize: 0, // 只对 resize 启用后有效,用于设置可拖动界限范围,如果为负数则允许拖动超出屏幕边界 123 marginSize: 0, // 只对 resize 启用后有效,用于设置可拖动界限范围,如果为负数则允许拖动超出屏幕边界
yudao-ui-admin-vue3/src/views/system/menu/index.vue
1 -<script setup lang="ts">  
2 -import * as MenuApi from '@/api/system/menu'  
3 -import { MenuVO } from '@/api/system/menu/types'  
4 -import { useI18n } from '@/hooks/web/useI18n'  
5 -import { useMessage } from '@/hooks/web/useMessage'  
6 -import { IconSelect } from '@/components/Icon'  
7 -import { Tooltip } from '@/components/Tooltip'  
8 -import { required } from '@/utils/formRules.js'  
9 -import { onMounted, reactive, ref } from 'vue'  
10 -import { VxeTableInstance } from 'vxe-table'  
11 -import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'  
12 -import { SystemMenuTypeEnum, CommonStatusEnum } from '@/utils/constants'  
13 -import {  
14 - ElRow,  
15 - ElCol,  
16 - ElForm,  
17 - ElFormItem,  
18 - ElInput,  
19 - ElInputNumber,  
20 - ElSelect,  
21 - ElTreeSelect,  
22 - ElOption,  
23 - ElRadioGroup,  
24 - ElRadioButton  
25 -} from 'element-plus'  
26 -import { handleTree } from '@/utils/tree'  
27 -const { t } = useI18n() // 国际化  
28 -const message = useMessage()  
29 -const xTable = ref<VxeTableInstance>()  
30 -const tableLoading = ref(false)  
31 -const tableData = ref()  
32 -const actionLoading = ref(false) // 遮罩层  
33 -const actionType = ref('') // 操作按钮的类型  
34 -const dialogVisible = ref(false) // 是否显示弹出层  
35 -const dialogTitle = ref('edit') // 弹出层标题  
36 -const statusOption = ref() // 状态选项  
37 -const menuForm = ref<MenuVO>({  
38 - id: 0,  
39 - name: '',  
40 - permission: '',  
41 - type: SystemMenuTypeEnum.DIR,  
42 - sort: 1,  
43 - parentId: 0,  
44 - path: '',  
45 - icon: '',  
46 - component: '',  
47 - status: CommonStatusEnum.ENABLE,  
48 - visible: true,  
49 - keepAlive: true,  
50 - createTime: ''  
51 -})  
52 -const menuProps = {  
53 - checkStrictly: true,  
54 - children: 'children',  
55 - label: 'name',  
56 - value: 'id'  
57 -}  
58 -interface Tree {  
59 - id: number  
60 - name: string  
61 - children?: Tree[] | any[]  
62 -}  
63 -const menuOptions = ref<any[]>([]) // 树形结构  
64 -const getTree = async () => {  
65 - menuOptions.value = []  
66 - const res = await MenuApi.listSimpleMenusApi()  
67 - let menu: Tree = { id: 0, name: '主类目', children: [] }  
68 - menu.children = handleTree(res)  
69 - menuOptions.value.push(menu)  
70 -}  
71 -// ========== 查询 ==========  
72 -const queryParams = reactive({  
73 - name: null,  
74 - status: null  
75 -})  
76 -const getList = async () => {  
77 - statusOption.value = getIntDictOptions(DICT_TYPE.COMMON_STATUS)  
78 - tableLoading.value = true  
79 - const res = await MenuApi.getMenuListApi(queryParams)  
80 - tableData.value = res  
81 - tableLoading.value = false  
82 -}  
83 -// 设置标题  
84 -const setDialogTile = (type: string) => {  
85 - dialogTitle.value = t('action.' + type)  
86 - actionType.value = type  
87 - dialogVisible.value = true  
88 -}  
89 -// 新建操作  
90 -const handleCreate = () => {  
91 - setDialogTile('create')  
92 -}  
93 -// 修改操作  
94 -const handleUpdate = async (row: MenuVO) => {  
95 - // 设置数据  
96 - const res = await MenuApi.getMenuApi(row.id)  
97 - console.log(res)  
98 - menuForm.value = res  
99 - setDialogTile('update')  
100 -}  
101 -// 删除操作  
102 -const handleDelete = async (row: MenuVO) => {  
103 - message.confirm(t('common.delDataMessage'), t('common.confirmTitle')).then(async () => {  
104 - await MenuApi.deleteMenuApi(row.id)  
105 - message.success(t('common.delSuccess'))  
106 - await getList()  
107 - })  
108 -}  
109 -// 表单校验  
110 -const rules = reactive({  
111 - name: [required],  
112 - sort: [required],  
113 - path: [required],  
114 - status: [required]  
115 -})  
116 -// 查询操作  
117 -const handleQuery = async () => {  
118 - await getList()  
119 -}  
120 -// 重置操作  
121 -const resetQuery = async () => {  
122 - queryParams.name = null  
123 - queryParams.status = null  
124 - await getList()  
125 -}  
126 -// 保存操作  
127 -const isExternal = (path: string) => {  
128 - return /^(https?:|mailto:|tel:)/.test(path)  
129 -}  
130 -const submitForm = async () => {  
131 - actionLoading.value = true  
132 - // 提交请求  
133 - try {  
134 - if (  
135 - menuForm.value.type === SystemMenuTypeEnum.DIR ||  
136 - menuForm.value.type === SystemMenuTypeEnum.MENU  
137 - ) {  
138 - if (!isExternal(menuForm.value.path)) {  
139 - if (menuForm.value.parentId === 0 && menuForm.value.path.charAt(0) !== '/') {  
140 - message.error('路径必须以 / 开头')  
141 - return  
142 - } else if (menuForm.value.parentId !== 0 && menuForm.value.path.charAt(0) === '/') {  
143 - message.error('路径不能以 / 开头')  
144 - return  
145 - }  
146 - }  
147 - }  
148 - if (actionType.value === 'create') {  
149 - await MenuApi.createMenuApi(menuForm.value)  
150 - message.success(t('common.createSuccess'))  
151 - } else {  
152 - await MenuApi.updateMenuApi(menuForm.value)  
153 - message.success(t('common.updateSuccess'))  
154 - }  
155 - // 操作成功,重新加载列表  
156 - dialogVisible.value = false  
157 - await getList()  
158 - } finally {  
159 - actionLoading.value = false  
160 - }  
161 -}  
162 -onMounted(async () => {  
163 - await getList()  
164 - getTree()  
165 -})  
166 -</script>  
167 <template> 1 <template>
168 <ContentWrap> 2 <ContentWrap>
169 <el-form :model="queryParams" ref="queryForm" :inline="true"> 3 <el-form :model="queryParams" ref="queryForm" :inline="true">
@@ -211,6 +45,7 @@ onMounted(async () =&gt; { @@ -211,6 +45,7 @@ onMounted(async () =&gt; {
211 :print-config="{}" 45 :print-config="{}"
212 :export-config="{}" 46 :export-config="{}"
213 :data="tableData" 47 :data="tableData"
  48 + class="xtable"
214 > 49 >
215 <vxe-column title="菜单名称" field="name" width="200" tree-node> 50 <vxe-column title="菜单名称" field="name" width="200" tree-node>
216 <template #default="{ row }"> 51 <template #default="{ row }">
@@ -255,19 +90,7 @@ onMounted(async () =&gt; { @@ -255,19 +90,7 @@ onMounted(async () =&gt; {
255 </vxe-column> 90 </vxe-column>
256 </vxe-table> 91 </vxe-table>
257 </ContentWrap> 92 </ContentWrap>
258 - <vxe-modal  
259 - v-model="dialogVisible"  
260 - id="menuModel"  
261 - :title="dialogTitle"  
262 - width="800"  
263 - height="6f00"  
264 - min-width="460"  
265 - min-height="320"  
266 - show-zoom  
267 - resize  
268 - transfer  
269 - show-footer  
270 - > 93 + <XModal v-model="dialogVisible" id="menuModel" :title="dialogTitle">
271 <template #default> 94 <template #default>
272 <!-- 对话框(添加 / 修改) --> 95 <!-- 对话框(添加 / 修改) -->
273 <el-form 96 <el-form
@@ -410,10 +233,175 @@ onMounted(async () =&gt; { @@ -410,10 +233,175 @@ onMounted(async () =&gt; {
410 type="primary" 233 type="primary"
411 :loading="actionLoading" 234 :loading="actionLoading"
412 @click="submitForm" 235 @click="submitForm"
413 - >  
414 - {{ t('action.save') }}  
415 - </el-button>  
416 - <el-button @click="dialogVisible = false">{{ t('dialog.close') }}</el-button> 236 + :content="t('action.save')"
  237 + />
  238 + <el-button @click="dialogVisible = false" :content="t('dialog.close')" />
417 </template> 239 </template>
418 - </vxe-modal> 240 + </XModal>
419 </template> 241 </template>
  242 +<script setup lang="ts">
  243 +import * as MenuApi from '@/api/system/menu'
  244 +import { MenuVO } from '@/api/system/menu/types'
  245 +import { useI18n } from '@/hooks/web/useI18n'
  246 +import { useMessage } from '@/hooks/web/useMessage'
  247 +import { IconSelect } from '@/components/Icon'
  248 +import { Tooltip } from '@/components/Tooltip'
  249 +import { required } from '@/utils/formRules.js'
  250 +import { onMounted, reactive, ref } from 'vue'
  251 +import { VxeTableInstance } from 'vxe-table'
  252 +import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
  253 +import { SystemMenuTypeEnum, CommonStatusEnum } from '@/utils/constants'
  254 +import {
  255 + ElRow,
  256 + ElCol,
  257 + ElForm,
  258 + ElFormItem,
  259 + ElInput,
  260 + ElInputNumber,
  261 + ElSelect,
  262 + ElTreeSelect,
  263 + ElOption,
  264 + ElRadioGroup,
  265 + ElRadioButton
  266 +} from 'element-plus'
  267 +import { handleTree } from '@/utils/tree'
  268 +const { t } = useI18n() // 国际化
  269 +const message = useMessage()
  270 +const xTable = ref<VxeTableInstance>()
  271 +const tableLoading = ref(false)
  272 +const tableData = ref()
  273 +const actionLoading = ref(false) // 遮罩层
  274 +const actionType = ref('') // 操作按钮的类型
  275 +const dialogVisible = ref(false) // 是否显示弹出层
  276 +const dialogTitle = ref('edit') // 弹出层标题
  277 +const statusOption = ref() // 状态选项
  278 +const menuForm = ref<MenuVO>({
  279 + id: 0,
  280 + name: '',
  281 + permission: '',
  282 + type: SystemMenuTypeEnum.DIR,
  283 + sort: 1,
  284 + parentId: 0,
  285 + path: '',
  286 + icon: '',
  287 + component: '',
  288 + status: CommonStatusEnum.ENABLE,
  289 + visible: true,
  290 + keepAlive: true,
  291 + createTime: ''
  292 +})
  293 +const menuProps = {
  294 + checkStrictly: true,
  295 + children: 'children',
  296 + label: 'name',
  297 + value: 'id'
  298 +}
  299 +interface Tree {
  300 + id: number
  301 + name: string
  302 + children?: Tree[] | any[]
  303 +}
  304 +const menuOptions = ref<any[]>([]) // 树形结构
  305 +const getTree = async () => {
  306 + menuOptions.value = []
  307 + const res = await MenuApi.listSimpleMenusApi()
  308 + let menu: Tree = { id: 0, name: '主类目', children: [] }
  309 + menu.children = handleTree(res)
  310 + menuOptions.value.push(menu)
  311 +}
  312 +// ========== 查询 ==========
  313 +const queryParams = reactive({
  314 + name: null,
  315 + status: null
  316 +})
  317 +const getList = async () => {
  318 + statusOption.value = getIntDictOptions(DICT_TYPE.COMMON_STATUS)
  319 + tableLoading.value = true
  320 + const res = await MenuApi.getMenuListApi(queryParams)
  321 + tableData.value = res
  322 + tableLoading.value = false
  323 +}
  324 +// 设置标题
  325 +const setDialogTile = (type: string) => {
  326 + dialogTitle.value = t('action.' + type)
  327 + actionType.value = type
  328 + dialogVisible.value = true
  329 +}
  330 +// 新建操作
  331 +const handleCreate = () => {
  332 + setDialogTile('create')
  333 +}
  334 +// 修改操作
  335 +const handleUpdate = async (row: MenuVO) => {
  336 + // 设置数据
  337 + const res = await MenuApi.getMenuApi(row.id)
  338 + console.log(res)
  339 + menuForm.value = res
  340 + setDialogTile('update')
  341 +}
  342 +// 删除操作
  343 +const handleDelete = async (row: MenuVO) => {
  344 + message.confirm(t('common.delDataMessage'), t('common.confirmTitle')).then(async () => {
  345 + await MenuApi.deleteMenuApi(row.id)
  346 + message.success(t('common.delSuccess'))
  347 + await getList()
  348 + })
  349 +}
  350 +// 表单校验
  351 +const rules = reactive({
  352 + name: [required],
  353 + sort: [required],
  354 + path: [required],
  355 + status: [required]
  356 +})
  357 +// 查询操作
  358 +const handleQuery = async () => {
  359 + await getList()
  360 +}
  361 +// 重置操作
  362 +const resetQuery = async () => {
  363 + queryParams.name = null
  364 + queryParams.status = null
  365 + await getList()
  366 +}
  367 +// 保存操作
  368 +const isExternal = (path: string) => {
  369 + return /^(https?:|mailto:|tel:)/.test(path)
  370 +}
  371 +const submitForm = async () => {
  372 + actionLoading.value = true
  373 + // 提交请求
  374 + try {
  375 + if (
  376 + menuForm.value.type === SystemMenuTypeEnum.DIR ||
  377 + menuForm.value.type === SystemMenuTypeEnum.MENU
  378 + ) {
  379 + if (!isExternal(menuForm.value.path)) {
  380 + if (menuForm.value.parentId === 0 && menuForm.value.path.charAt(0) !== '/') {
  381 + message.error('路径必须以 / 开头')
  382 + return
  383 + } else if (menuForm.value.parentId !== 0 && menuForm.value.path.charAt(0) === '/') {
  384 + message.error('路径不能以 / 开头')
  385 + return
  386 + }
  387 + }
  388 + }
  389 + if (actionType.value === 'create') {
  390 + await MenuApi.createMenuApi(menuForm.value)
  391 + message.success(t('common.createSuccess'))
  392 + } else {
  393 + await MenuApi.updateMenuApi(menuForm.value)
  394 + message.success(t('common.updateSuccess'))
  395 + }
  396 + // 操作成功,重新加载列表
  397 + dialogVisible.value = false
  398 + await getList()
  399 + } finally {
  400 + actionLoading.value = false
  401 + }
  402 +}
  403 +onMounted(async () => {
  404 + await getList()
  405 + getTree()
  406 +})
  407 +</script>
yudao-ui-admin-vue3/src/views/system/post/index.vue
  1 +<template>
  2 + <ContentWrap>
  3 + <vxe-grid ref="xGrid" v-bind="gridOptions" class="xtable-scrollbar">
  4 + <template #toolbar_buttons>
  5 + <el-button type="primary" v-hasPermi="['system:post:create']" @click="handleCreate">
  6 + <Icon icon="ep:zoom-in" class="mr-5px" /> {{ t('action.add') }}
  7 + </el-button>
  8 + </template>
  9 + <template #status_default="{ row }">
  10 + <DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
  11 + </template>
  12 + <template #action_default="{ row }">
  13 + <el-button
  14 + link
  15 + type="primary"
  16 + v-hasPermi="['system:post:update']"
  17 + @click="handleUpdate(row.id)"
  18 + >
  19 + <Icon icon="ep:edit" class="mr-1px" /> {{ t('action.edit') }}
  20 + </el-button>
  21 + <el-button
  22 + link
  23 + type="primary"
  24 + v-hasPermi="['system:post:update']"
  25 + @click="handleDetail(row)"
  26 + >
  27 + <Icon icon="ep:view" class="mr-1px" /> {{ t('action.detail') }}
  28 + </el-button>
  29 + <el-button
  30 + link
  31 + type="primary"
  32 + v-hasPermi="['system:post:delete']"
  33 + @click="handleDelete(row.id)"
  34 + >
  35 + <Icon icon="ep:delete" class="mr-1px" /> {{ t('action.del') }}
  36 + </el-button>
  37 + </template>
  38 + </vxe-grid>
  39 + </ContentWrap>
  40 + <XModal id="postModel" v-model="dialogVisible" :title="dialogTitle">
  41 + <template #default>
  42 + <!-- 对话框(添加 / 修改) -->
  43 + <vxe-form
  44 + ref="xForm"
  45 + v-if="['create', 'update'].includes(actionType)"
  46 + :data="formData"
  47 + :items="formItems"
  48 + :rules="rules"
  49 + />
  50 + <Descriptions
  51 + v-if="actionType === 'detail'"
  52 + :schema="allSchemas.detailSchema"
  53 + :data="detailRef"
  54 + >
  55 + <template #status="{ row }">
  56 + <DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
  57 + </template>
  58 + <template #createTime="{ row }">
  59 + <span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
  60 + </template>
  61 + </Descriptions>
  62 + </template>
  63 + <template #footer>
  64 + <vxe-button
  65 + v-if="['create', 'update'].includes(actionType)"
  66 + status="primary"
  67 + @click="submitForm"
  68 + :content="t('action.save')"
  69 + />
  70 + <vxe-button @click="dialogVisible = false" :content="t('dialog.close')" />
  71 + </template>
  72 + </XModal>
  73 +</template>
1 <script setup lang="ts"> 74 <script setup lang="ts">
2 import { ref } from 'vue' 75 import { ref } from 'vue'
3 import dayjs from 'dayjs' 76 import dayjs from 'dayjs'
@@ -82,89 +155,3 @@ const submitForm: VxeFormEvents.Submit = async () =&gt; { @@ -82,89 +155,3 @@ const submitForm: VxeFormEvents.Submit = async () =&gt; {
82 } 155 }
83 } 156 }
84 </script> 157 </script>
85 -<template>  
86 - <ContentWrap>  
87 - <vxe-grid ref="xGrid" v-bind="gridOptions">  
88 - <template #toolbar_buttons>  
89 - <el-button type="primary" v-hasPermi="['system:post:create']" @click="handleCreate">  
90 - <Icon icon="ep:zoom-in" class="mr-5px" /> {{ t('action.add') }}  
91 - </el-button>  
92 - </template>  
93 - <template #status_default="{ row }">  
94 - <DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />  
95 - </template>  
96 - <template #action_default="{ row }">  
97 - <el-button  
98 - link  
99 - type="primary"  
100 - v-hasPermi="['system:post:update']"  
101 - @click="handleUpdate(row.id)"  
102 - >  
103 - <Icon icon="ep:edit" class="mr-1px" /> {{ t('action.edit') }}  
104 - </el-button>  
105 - <el-button  
106 - link  
107 - type="primary"  
108 - v-hasPermi="['system:post:update']"  
109 - @click="handleDetail(row)"  
110 - >  
111 - <Icon icon="ep:view" class="mr-1px" /> {{ t('action.detail') }}  
112 - </el-button>  
113 - <el-button  
114 - link  
115 - type="primary"  
116 - v-hasPermi="['system:post:delete']"  
117 - @click="handleDelete(row.id)"  
118 - >  
119 - <Icon icon="ep:delete" class="mr-1px" /> {{ t('action.del') }}  
120 - </el-button>  
121 - </template>  
122 - </vxe-grid>  
123 - </ContentWrap>  
124 - <vxe-modal  
125 - v-model="dialogVisible"  
126 - id="postModel"  
127 - :title="dialogTitle"  
128 - width="800"  
129 - height="400"  
130 - min-width="460"  
131 - min-height="320"  
132 - show-zoom  
133 - resize  
134 - transfer  
135 - show-footer  
136 - >  
137 - <template #default>  
138 - <!-- 对话框(添加 / 修改) -->  
139 - <vxe-form  
140 - ref="xForm"  
141 - v-if="['create', 'update'].includes(actionType)"  
142 - :data="formData"  
143 - :items="formItems"  
144 - :rules="rules"  
145 - />  
146 - <Descriptions  
147 - v-if="actionType === 'detail'"  
148 - :schema="allSchemas.detailSchema"  
149 - :data="detailRef"  
150 - >  
151 - <template #status="{ row }">  
152 - <DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />  
153 - </template>  
154 - <template #createTime="{ row }">  
155 - <span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>  
156 - </template>  
157 - </Descriptions>  
158 - </template>  
159 - <template #footer>  
160 - <vxe-button  
161 - v-if="['create', 'update'].includes(actionType)"  
162 - status="primary"  
163 - @click="submitForm"  
164 - >  
165 - {{ t('action.save') }}  
166 - </vxe-button>  
167 - <vxe-button @click="dialogVisible = false">{{ t('dialog.close') }}</vxe-button>  
168 - </template>  
169 - </vxe-modal>  
170 -</template>