This commit is contained in:
zlwu committed 2026-07-05 10:07:24 +08:00
1 parent 33c038b8d9
commit 54b27a3f0e
253 files changed
+30060 -14298

No files matched your search

+60 -4
View File
@@ -33,6 +33,12 @@
<div class="list-toolbar-selected">
已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
</div>
<ListQuickSearchInput
v-model="quickSearchKeyword"
placeholder="组织名称"
@search="handleQuickSearch"
@clear="handleQuickSearchClear"
/>
<el-button class="btn-main" type="primary" @click="handleAdd">
<el-icon><Plus /></el-icon>
新增
@@ -67,7 +73,15 @@
{{ isExpandAll ? '全部折叠' : '全部展开' }}
</el-button>
<ListToolbarEndActions :loading="loading" @refresh="loadData" />
<ListToolbarEndActions
:loading="loading"
:column-defs="listColumnDefs"
v-model:visible-columns="visibleColumns"
v-model:check-all="checkAll"
:is-indeterminate="isIndeterminate"
@refresh="loadData"
@reset-columns="resetToDefault"
/>
</div>
</div>
@@ -105,6 +119,7 @@
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('deptCategoryCode')"
prop="deptCategoryCode"
label="组织类别"
min-width="120"
@@ -116,6 +131,7 @@
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('status')"
prop="status"
label="状态"
min-width="80"
@@ -129,6 +145,7 @@
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('leader')"
prop="leader"
label="负责人"
min-width="160"
@@ -136,6 +153,7 @@
header-align="center"
/>
<el-table-column
v-if="isColumnVisible('leaderPhone')"
prop="leaderPhone"
label="联系电话"
min-width="180"
@@ -159,8 +177,9 @@
ref="formRef"
:model="formData"
:rules="formRules"
label-position="top"
class="dept-dialog-form"
label-position="right"
label-width="100px"
class="dept-dialog-form sys-dialog-form"
scroll-to-error
:scroll-into-view-options="{ behavior: 'smooth', block: 'center' }"
>
@@ -284,8 +303,11 @@
<script setup lang="ts">
import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'
import ListQuickSearchInput from '@/components/business/list-quick-search-input/index.vue'
import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
import { ref, reactive, computed, onMounted, nextTick } from 'vue'
import { canDeleteByCreator, filterDeletableByCreator, allRowsDeletableByCreator, CREATOR_DELETE_TIP } from '@/utils/creator-delete'
import { syncViewRowFromTree } from '@/utils/view-dialog-refresh'
import { ElMessage, ElMessageBox } from 'element-plus'
import {
Plus,
@@ -311,9 +333,25 @@ import { useDictStore } from '@/store/modules/dict'
defineOptions({ name: 'SystemDept' })
const listColumnDefs = [
{ prop: 'deptCategoryCode', label: '组织类别' },
{ prop: 'status', label: '状态' },
{ prop: 'leader', label: '负责人' },
{ prop: 'leaderPhone', label: '联系电话' }
]
const {
visibleColumns,
checkAll,
isIndeterminate,
isColumnVisible,
resetToDefault
} = usePersistedListColumns('system-dept-list-columns', listColumnDefs)
const dictStore = useDictStore()
const searchForm = reactive<{ keyword: string }>({ keyword: '' })
const quickSearchKeyword = ref('')
const tableData = ref<Api.Dept.DeptItem[]>([])
/** 弹窗上级组织:全量树(不受列表关键词筛选影响) */
const deptTreeForSelect = ref<Api.Dept.DeptItem[]>([])
@@ -333,6 +371,11 @@ const dictStore = useDictStore()
const viewVisible = ref(false)
const viewRow = ref<Api.Dept.DeptItem | null>(null)
function refreshViewIfOpen() {
if (!viewVisible.value) return
syncViewRowFromTree(viewRow, tableData.value)
}
const formData = reactive({
parentId: '',
deptName: '',
@@ -595,8 +638,9 @@ const dictStore = useDictStore()
async function loadData() {
loading.value = true
try {
const quickKeyword = quickSearchKeyword.value?.trim()
const res = await fetchDeptTree({
keyword: searchForm.keyword || undefined
keyword: quickKeyword || searchForm.keyword || undefined
})
tableData.value = res || []
await nextTick()
@@ -609,11 +653,22 @@ const dictStore = useDictStore()
}
function handleSearch() {
quickSearchKeyword.value = ''
loadData()
}
function handleReset() {
searchForm.keyword = ''
quickSearchKeyword.value = ''
loadData()
}
function handleQuickSearch() {
loadData()
}
function handleQuickSearchClear() {
quickSearchKeyword.value = ''
loadData()
}
@@ -709,6 +764,7 @@ const dictStore = useDictStore()
ElMessage.success(isEdit.value ? '更新成功' : '创建成功')
dialogVisible.value = false
await Promise.all([loadData(), loadDeptTreeForSelect()])
if (isEdit.value) refreshViewIfOpen()
} catch {
/* 校验或接口错误已由全局处理 */
} finally {
+89 -6
View File
@@ -32,6 +32,12 @@
<div class="list-toolbar-selected">
已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
</div>
<ListQuickSearchInput
v-model="quickSearchKeyword"
placeholder="字典名称/类型编码"
@search="handleQuickSearch"
@clear="handleQuickSearchClear"
/>
<el-button class="btn-main" type="primary" @click="handleAdd">
<el-icon><Plus /></el-icon>
新增
@@ -73,7 +79,15 @@
批量删除
</el-button>
<ListToolbarEndActions :loading="loading" @refresh="loadData" />
<ListToolbarEndActions
:loading="loading"
:column-defs="listColumnDefs"
v-model:visible-columns="visibleColumns"
v-model:check-all="checkAll"
:is-indeterminate="isIndeterminate"
@refresh="loadData"
@reset-columns="resetToDefault"
/>
</div>
</div>
@@ -108,6 +122,7 @@
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('dictType')"
prop="dictType"
label="字典类型编码"
min-width="150"
@@ -115,6 +130,7 @@
header-align="center"
/>
<el-table-column
v-if="isColumnVisible('remark')"
prop="remark"
label="备注"
min-width="150"
@@ -122,6 +138,7 @@
header-align="center"
/>
<el-table-column
v-if="isColumnVisible('createTime')"
prop="createTime"
label="创建时间"
min-width="180"
@@ -155,8 +172,9 @@
ref="formRef"
:model="formData"
:rules="formRules"
label-position="top"
class="dict-dialog-form"
label-position="right"
label-width="100px"
class="dict-dialog-form sys-dialog-form"
scroll-to-error
:scroll-into-view-options="{ behavior: 'smooth', block: 'center' }"
>
@@ -323,6 +341,15 @@
<el-icon><Delete /></el-icon>
删除
</el-button>
<ListToolbarEndActions
:loading="dictDataLoading"
:column-defs="dictDataListColumnDefs"
v-model:visible-columns="dictDataVisibleColumns"
v-model:check-all="dictDataCheckAll"
:is-indeterminate="dictDataIsIndeterminate"
@refresh="loadDictData"
@reset-columns="resetDictDataColumnsToDefault"
/>
</div>
</div>
<div class="list-body list-wrap dict-data-table-wrap">
@@ -353,6 +380,7 @@
header-align="center"
/>
<el-table-column
v-if="isDictDataColumnVisible('dictValue')"
prop="dictValue"
label="数据值"
min-width="120"
@@ -360,6 +388,7 @@
header-align="center"
/>
<el-table-column
v-if="isDictDataColumnVisible('tagType')"
prop="tagType"
label="标签颜色"
min-width="100"
@@ -378,6 +407,7 @@
</template>
</el-table-column>
<el-table-column
v-if="isDictDataColumnVisible('remark')"
prop="remark"
label="备注"
min-width="150"
@@ -419,8 +449,9 @@
ref="dictDataFormRef"
:model="dictDataFormData"
:rules="dictDataFormRules"
label-position="top"
class="dict-dialog-form"
label-position="right"
label-width="100px"
class="dict-dialog-form sys-dialog-form"
scroll-to-error
:scroll-into-view-options="{ behavior: 'smooth', block: 'center' }"
>
@@ -485,8 +516,11 @@
<script setup lang="ts">
import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'
import ListQuickSearchInput from '@/components/business/list-quick-search-input/index.vue'
import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
import { ref, reactive, onMounted, computed } from 'vue'
import { canDeleteByCreator, filterDeletableByCreator, allRowsDeletableByCreator, CREATOR_DELETE_TIP } from '@/utils/creator-delete'
import { syncViewRowFromFlatList } from '@/utils/view-dialog-refresh'
import { ElMessage, ElMessageBox } from 'element-plus'
import {
Plus,
@@ -514,7 +548,36 @@ import ListToolbarEndActions from '@/components/business/list-toolbar-end-action
defineOptions({ name: 'SystemDict' })
const listColumnDefs = [
{ prop: 'dictType', label: '字典类型编码' },
{ prop: 'remark', label: '备注' },
{ prop: 'createTime', label: '创建时间' }
]
const {
visibleColumns,
checkAll,
isIndeterminate,
isColumnVisible,
resetToDefault
} = usePersistedListColumns('system-dict-list-columns', listColumnDefs)
const dictDataListColumnDefs = [
{ prop: 'dictValue', label: '数据值' },
{ prop: 'tagType', label: '标签颜色' },
{ prop: 'remark', label: '备注' }
]
const {
visibleColumns: dictDataVisibleColumns,
checkAll: dictDataCheckAll,
isIndeterminate: dictDataIsIndeterminate,
isColumnVisible: isDictDataColumnVisible,
resetToDefault: resetDictDataColumnsToDefault
} = usePersistedListColumns('system-dict-data-list-columns', dictDataListColumnDefs)
const searchForm = reactive<{ keyword: string }>({ keyword: '' })
const quickSearchKeyword = ref('')
const tableData = ref<Api.Dict.DictTypeItem[]>([])
const selectedRows = ref<Api.Dict.DictTypeItem[]>([])
const loading = ref(false)
@@ -528,6 +591,11 @@ import ListToolbarEndActions from '@/components/business/list-toolbar-end-action
const viewVisible = ref(false)
const viewRow = ref<Api.Dict.DictTypeItem | null>(null)
function refreshViewIfOpen() {
if (!viewVisible.value) return
syncViewRowFromFlatList(viewRow, tableData.value)
}
const viewDictDataList = ref<Api.Dict.DictDataItem[]>([])
const viewDictDataLoading = ref(false)
@@ -550,10 +618,11 @@ import ListToolbarEndActions from '@/components/business/list-toolbar-end-action
async function loadData() {
loading.value = true
try {
const quickKeyword = quickSearchKeyword.value?.trim()
const res = await fetchDictTypeList({
page: pagination.page,
pageSize: pagination.pageSize,
keyword: searchForm.keyword || undefined
keyword: quickKeyword || searchForm.keyword || undefined
})
tableData.value = res.list || []
pagination.total = res.total ?? 0
@@ -563,12 +632,25 @@ import ListToolbarEndActions from '@/components/business/list-toolbar-end-action
}
function handleSearch() {
quickSearchKeyword.value = ''
pagination.page = 1
loadData()
}
function handleReset() {
searchForm.keyword = ''
quickSearchKeyword.value = ''
pagination.page = 1
loadData()
}
function handleQuickSearch() {
pagination.page = 1
loadData()
}
function handleQuickSearchClear() {
quickSearchKeyword.value = ''
pagination.page = 1
loadData()
}
@@ -651,6 +733,7 @@ import ListToolbarEndActions from '@/components/business/list-toolbar-end-action
}
dialogVisible.value = false
await loadData()
if (isEdit.value) refreshViewIfOpen()
} catch {
/* 校验或接口错误已由全局处理 */
} finally {
+74 -5
View File
@@ -45,6 +45,12 @@
<div class="list-toolbar-selected">
已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
</div>
<ListQuickSearchInput
v-model="quickSearchKeyword"
placeholder="请假原因/类型"
@search="handleQuickSearch"
@clear="handleQuickSearchClear"
/>
<el-button class="btn-main" type="primary" @click="handleAdd">
<el-icon><Plus /></el-icon>
新增请假
@@ -76,7 +82,15 @@
删除
</el-button>
<ListToolbarEndActions :loading="loading" @refresh="loadData" />
<ListToolbarEndActions
:loading="loading"
:column-defs="listColumnDefs"
v-model:visible-columns="visibleColumns"
v-model:check-all="checkAll"
:is-indeterminate="isIndeterminate"
@refresh="loadData"
@reset-columns="resetToDefault"
/>
</div>
</div>
@@ -111,6 +125,7 @@
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('startDate')"
prop="startDate"
label="开始日期"
min-width="120"
@@ -118,6 +133,7 @@
header-align="center"
/>
<el-table-column
v-if="isColumnVisible('endDate')"
prop="endDate"
label="结束日期"
min-width="120"
@@ -125,14 +141,22 @@
header-align="center"
/>
<el-table-column
v-if="isColumnVisible('days')"
prop="days"
label="天数"
min-width="80"
align="center"
header-align="center"
/>
<el-table-column prop="reason" label="请假原因" min-width="180" show-overflow-tooltip />
<el-table-column
v-if="isColumnVisible('reason')"
prop="reason"
label="请假原因"
min-width="180"
show-overflow-tooltip
/>
<el-table-column
v-if="isColumnVisible('status')"
prop="status"
label="状态"
min-width="100"
@@ -146,6 +170,7 @@
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('createTime')"
prop="createTime"
label="创建时间"
min-width="180"
@@ -179,7 +204,9 @@
>
<el-form
:model="formData"
label-position="top"
label-position="right"
label-width="100px"
class="wf-dialog-form"
scroll-to-error
:scroll-into-view-options="{ behavior: 'smooth', block: 'center' }"
>
@@ -279,10 +306,13 @@
</template>
<script setup lang="ts">
import ListQuickSearchInput from '@/components/business/list-quick-search-input/index.vue'
import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'
import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
import { Close, Check, RefreshLeft, Search, Plus, Edit, Delete, Promotion, Document } from '@element-plus/icons-vue'
import { useDictStore } from '@/store/modules/dict'
import { canDeleteByCreator, CREATOR_DELETE_TIP } from '@/utils/creator-delete'
import { syncViewRowFromFlatList } from '@/utils/view-dialog-refresh'
import SystemViewDialog, {
type ViewField,
type ViewSection
@@ -297,6 +327,23 @@ import ListToolbarEndActions from '@/components/business/list-toolbar-end-action
defineOptions({ name: 'LeaveRequest' })
const listColumnDefs = [
{ prop: 'startDate', label: '开始日期' },
{ prop: 'endDate', label: '结束日期' },
{ prop: 'days', label: '天数' },
{ prop: 'reason', label: '请假原因' },
{ prop: 'status', label: '状态' },
{ prop: 'createTime', label: '创建时间' }
]
const {
visibleColumns,
checkAll,
isIndeterminate,
isColumnVisible,
resetToDefault
} = usePersistedListColumns('workflow-leave-list-columns', listColumnDefs)
const dictStore = useDictStore()
const loading = ref(false)
@@ -305,6 +352,7 @@ import ListToolbarEndActions from '@/components/business/list-toolbar-end-action
const pagination = reactive({ page: 1, pageSize: 20, total: 0 })
const filterForm = reactive({ keyword: '', status: '' })
const quickSearchKeyword = ref('')
const dialogVisible = ref(false)
const isEdit = ref(false)
@@ -314,6 +362,11 @@ import ListToolbarEndActions from '@/components/business/list-toolbar-end-action
const viewVisible = ref(false)
const viewingRecord = ref<Api.Leave.LeaveItem | null>(null)
function refreshViewIfOpen() {
if (!viewVisible.value) return
syncViewRowFromFlatList(viewingRecord, tableData.value)
}
const leaveViewSections = computed<ViewSection[]>(() => {
const row = viewingRecord.value
if (!row) return []
@@ -384,10 +437,11 @@ import ListToolbarEndActions from '@/components/business/list-toolbar-end-action
async function loadData() {
loading.value = true
try {
const quickKeyword = quickSearchKeyword.value?.trim()
const res = await fetchLeaveList({
page: pagination.page,
pageSize: pagination.pageSize,
keyword: filterForm.keyword || undefined,
keyword: quickKeyword || filterForm.keyword || undefined,
status: filterForm.status || undefined
})
tableData.value = res.list || []
@@ -400,6 +454,7 @@ import ListToolbarEndActions from '@/components/business/list-toolbar-end-action
}
function handleSearch() {
quickSearchKeyword.value = ''
pagination.page = 1
loadData()
}
@@ -407,6 +462,18 @@ import ListToolbarEndActions from '@/components/business/list-toolbar-end-action
function handleReset() {
filterForm.keyword = ''
filterForm.status = ''
quickSearchKeyword.value = ''
pagination.page = 1
loadData()
}
function handleQuickSearch() {
pagination.page = 1
loadData()
}
function handleQuickSearchClear() {
quickSearchKeyword.value = ''
pagination.page = 1
loadData()
}
@@ -472,7 +539,8 @@ import ListToolbarEndActions from '@/components/business/list-toolbar-end-action
await fetchCreateLeave({ ...formData })
}
dialogVisible.value = false
loadData()
await loadData()
if (isEdit.value) refreshViewIfOpen()
} catch {
// error shown by http util
} finally {
@@ -517,6 +585,7 @@ import ListToolbarEndActions from '@/components/business/list-toolbar-end-action
watchDebounced(
filterForm,
() => {
quickSearchKeyword.value = ''
pagination.page = 1
loadData()
},
+64 -4
View File
@@ -32,6 +32,12 @@
<div class="list-toolbar-selected">
已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
</div>
<ListQuickSearchInput
v-model="quickSearchKeyword"
placeholder="菜单名称"
@search="handleQuickSearch"
@clear="handleQuickSearchClear"
/>
<el-button class="btn-main" type="primary" @click="handleAdd">
<el-icon><Plus /></el-icon>
新增
@@ -66,7 +72,15 @@
{{ isExpandAll ? '全部折叠' : '全部展开' }}
</el-button>
<ListToolbarEndActions :loading="loading" @refresh="loadData" />
<ListToolbarEndActions
:loading="loading"
:column-defs="listColumnDefs"
v-model:visible-columns="visibleColumns"
v-model:check-all="checkAll"
:is-indeterminate="isIndeterminate"
@refresh="loadData"
@reset-columns="resetToDefault"
/>
</div>
</div>
@@ -106,6 +120,7 @@
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('menuType')"
prop="menuType"
label="类型"
min-width="90"
@@ -119,6 +134,7 @@
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('path')"
prop="path"
label="路由路径"
min-width="140"
@@ -130,6 +146,7 @@
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('component')"
prop="component"
label="组件路径"
min-width="160"
@@ -141,6 +158,7 @@
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('perms')"
prop="perms"
label="权限标识"
min-width="140"
@@ -152,6 +170,7 @@
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('visible')"
prop="visible"
label="可见"
min-width="80"
@@ -165,6 +184,7 @@
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('status')"
prop="status"
label="状态"
min-width="80"
@@ -194,8 +214,9 @@
ref="formRef"
:model="formData"
:rules="formRules"
label-position="top"
class="menu-dialog-form"
label-position="right"
label-width="100px"
class="menu-dialog-form sys-dialog-form"
scroll-to-error
:scroll-into-view-options="{ behavior: 'smooth', block: 'center' }"
>
@@ -295,8 +316,11 @@
<script setup lang="ts">
import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'
import ListQuickSearchInput from '@/components/business/list-quick-search-input/index.vue'
import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
import { ref, reactive, computed, onMounted, nextTick } from 'vue'
import { canDeleteByCreator, filterDeletableByCreator, allRowsDeletableByCreator, CREATOR_DELETE_TIP } from '@/utils/creator-delete'
import { syncViewRowFromTree } from '@/utils/view-dialog-refresh'
import { ElMessage, ElMessageBox } from 'element-plus'
import {
Plus,
@@ -314,9 +338,27 @@ import ListToolbarEndActions from '@/components/business/list-toolbar-end-action
defineOptions({ name: 'SystemMenu' })
const listColumnDefs = [
{ prop: 'menuType', label: '类型' },
{ prop: 'path', label: '路由路径' },
{ prop: 'component', label: '组件路径' },
{ prop: 'perms', label: '权限标识' },
{ prop: 'visible', label: '可见' },
{ prop: 'status', label: '状态' }
]
const {
visibleColumns,
checkAll,
isIndeterminate,
isColumnVisible,
resetToDefault
} = usePersistedListColumns('system-menu-list-columns', listColumnDefs)
const dictStore = useDictStore()
const searchForm = reactive<{ keyword: string }>({ keyword: '' })
const quickSearchKeyword = ref('')
const tableData = ref<Api.Menu.MenuItem[]>([])
const selectedRows = ref<Api.Menu.MenuItem[]>([])
const loading = ref(false)
@@ -333,6 +375,11 @@ import ListToolbarEndActions from '@/components/business/list-toolbar-end-action
const viewVisible = ref(false)
const viewRow = ref<Api.Menu.MenuItem | null>(null)
function refreshViewIfOpen() {
if (!viewVisible.value) return
syncViewRowFromTree(viewRow, tableData.value)
}
const formData = reactive({
parentId: '0',
menuName: '',
@@ -437,8 +484,9 @@ import ListToolbarEndActions from '@/components/business/list-toolbar-end-action
async function loadData() {
loading.value = true
try {
const quickKeyword = quickSearchKeyword.value?.trim()
const res = await fetchMenuTree({
keyword: searchForm.keyword || undefined
keyword: quickKeyword || searchForm.keyword || undefined
})
tableData.value = Array.isArray(res) ? res : []
await nextTick()
@@ -451,11 +499,22 @@ import ListToolbarEndActions from '@/components/business/list-toolbar-end-action
}
function handleSearch() {
quickSearchKeyword.value = ''
loadData()
}
function handleReset() {
searchForm.keyword = ''
quickSearchKeyword.value = ''
loadData()
}
function handleQuickSearch() {
loadData()
}
function handleQuickSearchClear() {
quickSearchKeyword.value = ''
loadData()
}
@@ -543,6 +602,7 @@ import ListToolbarEndActions from '@/components/business/list-toolbar-end-action
ElMessage.success(isEdit.value ? '更新成功' : '创建成功')
dialogVisible.value = false
await loadData()
if (isEdit.value) refreshViewIfOpen()
} catch {
/* 校验或接口错误已由全局处理 */
} finally {
+102 -23
View File
@@ -51,6 +51,12 @@
<div class="list-toolbar-selected">
已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
</div>
<ListQuickSearchInput
v-model="quickSearchKeyword"
placeholder="模块名称"
@search="handleQuickSearch"
@clear="handleQuickSearchClear"
/>
<el-button
class="btn-main"
type="primary"
@@ -61,7 +67,15 @@
查看详情
</el-button>
<ListToolbarEndActions :loading="loading" @refresh="handleSearch" />
<ListToolbarEndActions
:loading="loading"
:column-defs="listColumnDefs"
v-model:visible-columns="visibleColumns"
v-model:check-all="checkAll"
:is-indeterminate="isIndeterminate"
@refresh="handleSearch"
@reset-columns="resetToDefault"
/>
</div>
</div>
@@ -84,14 +98,6 @@
header-align="center"
:index="(index) => (pagination.page - 1) * pagination.pageSize + index + 1"
/>
<el-table-column prop="title" label="操作模块" min-width="140" show-overflow-tooltip />
<el-table-column label="操作类型" min-width="100" align="center" header-align="center">
<template #default="{ row }">
<el-tag :type="getOperLogBusinessTypeTagType(row.businessType)" size="small">
{{ getOperLogBusinessTypeLabel(row.businessType) }}
</el-tag>
</template>
</el-table-column>
<el-table-column
prop="operSummary"
label="操作摘要"
@@ -102,16 +108,60 @@
<el-link type="primary" @click="openDetail(row)">{{ row.operSummary || '—' }}</el-link>
</template>
</el-table-column>
<el-table-column prop="operName" label="操作人员" min-width="120" align="center" />
<el-table-column prop="operTime" label="操作时间" min-width="170" align="center" />
<el-table-column label="状态" min-width="90" align="center" header-align="center">
<el-table-column
v-if="isColumnVisible('title')"
prop="title"
label="操作模块"
min-width="140"
show-overflow-tooltip
/>
<el-table-column
v-if="isColumnVisible('businessType')"
label="操作类型"
min-width="100"
align="center"
header-align="center"
>
<template #default="{ row }">
<el-tag :type="getOperLogBusinessTypeTagType(row.businessType)" size="small">
{{ getOperLogBusinessTypeLabel(row.businessType) }}
</el-tag>
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('operName')"
prop="operName"
label="操作人员"
min-width="120"
align="center"
/>
<el-table-column
v-if="isColumnVisible('operTime')"
prop="operTime"
label="操作时间"
min-width="170"
align="center"
/>
<el-table-column
v-if="isColumnVisible('status')"
label="状态"
min-width="90"
align="center"
header-align="center"
>
<template #default="{ row }">
<el-tag :type="row.status === 0 ? 'success' : 'danger'" size="small">
{{ row.status === 0 ? '成功' : '失败' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="costTime" label="耗时(ms)" min-width="100" align="center" />
<el-table-column
v-if="isColumnVisible('costTime')"
prop="costTime"
label="耗时(ms)"
min-width="100"
align="center"
/>
</el-table>
<div class="pagination list-pager-bar">
@@ -183,18 +233,15 @@
<pre class="detail-pre error">{{ detailRow.errorMsg || '—' }}</pre>
</div>
</div>
<template #footer>
<el-button @click="detailVisible = false">
<el-icon><Close /></el-icon>关闭
</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'
import { Close, RefreshLeft, Search, View } from '@element-plus/icons-vue'
import ListQuickSearchInput from '@/components/business/list-quick-search-input/index.vue'
import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
import { RefreshLeft, Search, View } from '@element-plus/icons-vue'
import { list as listOperLog, detail as getOperLogDetail } from '@/api/monitor/operlog'
import type { OperLogVO, OperLogQuery } from '@/api/monitor/operlog/types'
import {
@@ -203,6 +250,23 @@ import {
getOperLogBusinessTypeTagType
} from '@/api/monitor/operlog/types'
const listColumnDefs = [
{ prop: 'title', label: '操作模块' },
{ prop: 'businessType', label: '操作类型' },
{ prop: 'operName', label: '操作人员' },
{ prop: 'operTime', label: '操作时间' },
{ prop: 'status', label: '状态' },
{ prop: 'costTime', label: '耗时(ms)', defaultVisible: false }
]
const {
visibleColumns,
checkAll,
isIndeterminate,
isColumnVisible,
resetToDefault
} = usePersistedListColumns('system-operlog-list-columns', listColumnDefs)
const loading = ref(false)
const tableData = ref<OperLogVO[]>([])
const selectedRows = ref<OperLogVO[]>([])
@@ -213,6 +277,7 @@ const searchForm = reactive({
operName: '',
status: undefined as number | undefined
})
const quickSearchKeyword = ref('')
const detailVisible = ref(false)
const detailRow = ref<OperLogVO | null>(null)
@@ -230,13 +295,14 @@ function handleToolbarView() {
}
function buildQuery(): OperLogQuery {
const quickKeyword = quickSearchKeyword.value?.trim()
return {
pageNum: pagination.page,
pageSize: pagination.pageSize,
title: searchForm.title || undefined,
businessType: searchForm.businessType,
operName: searchForm.operName || undefined,
status: searchForm.status
title: quickKeyword || searchForm.title || undefined,
businessType: quickKeyword ? undefined : searchForm.businessType,
operName: quickKeyword ? undefined : searchForm.operName || undefined,
status: quickKeyword ? undefined : searchForm.status
}
}
@@ -257,6 +323,7 @@ async function fetchList() {
}
function handleSearch() {
quickSearchKeyword.value = ''
pagination.page = 1
fetchList()
}
@@ -266,9 +333,21 @@ function handleReset() {
searchForm.businessType = undefined
searchForm.operName = ''
searchForm.status = undefined
quickSearchKeyword.value = ''
handleSearch()
}
function handleQuickSearch() {
pagination.page = 1
fetchList()
}
function handleQuickSearchClear() {
quickSearchKeyword.value = ''
pagination.page = 1
fetchList()
}
async function openDetail(row: OperLogVO) {
try {
const res = await getOperLogDetail({ id: String(row.id) })
+62 -6
View File
@@ -33,6 +33,12 @@
<div class="list-toolbar-selected">
已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
</div>
<ListQuickSearchInput
v-model="quickSearchKeyword"
placeholder="职位名称/编码"
@search="handleQuickSearch"
@clear="handleQuickSearchClear"
/>
<el-button class="btn-main" type="primary" @click="handleAdd">
<el-icon><Plus /></el-icon>
新增职位
@@ -74,7 +80,15 @@
批量删除
</el-button>
<ListToolbarEndActions :loading="loading" @refresh="loadData" />
<ListToolbarEndActions
:loading="loading"
:column-defs="listColumnDefs"
v-model:visible-columns="visibleColumns"
v-model:check-all="checkAll"
:is-indeterminate="isIndeterminate"
@refresh="loadData"
@reset-columns="resetToDefault"
/>
</div>
</div>
@@ -109,6 +123,7 @@
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('postCode')"
prop="postCode"
label="职位编码"
min-width="120"
@@ -116,6 +131,7 @@
header-align="center"
/>
<el-table-column
v-if="isColumnVisible('status')"
prop="status"
label="状态"
min-width="80"
@@ -129,6 +145,7 @@
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('createTime')"
prop="createTime"
label="创建时间"
min-width="180"
@@ -162,8 +179,9 @@
ref="formRef"
:model="formData"
:rules="formRules"
label-position="top"
class="post-dialog-form"
label-position="right"
label-width="100px"
class="post-dialog-form sys-dialog-form"
scroll-to-error
:scroll-into-view-options="{ behavior: 'smooth', block: 'center' }"
>
@@ -215,7 +233,7 @@
<p v-if="postBindPostName" class="post-bind-subtitle">
职位:<strong>{{ postBindPostName }}</strong>
</p>
<el-form label-position="top" class="data-scope-form">
<el-form label-position="right" label-width="100px" class="data-scope-form sys-dialog-form">
<el-form-item label="绑定用户" class="data-scope-custom-form-item">
<div class="data-scope-custom-grid">
<div class="data-scope-custom-col">
@@ -338,8 +356,11 @@
<script setup lang="ts">
import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'
import ListQuickSearchInput from '@/components/business/list-quick-search-input/index.vue'
import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
import { ref, reactive, computed, onMounted, nextTick } from 'vue'
import { canDeleteByCreator, filterDeletableByCreator, allRowsDeletableByCreator, CREATOR_DELETE_TIP } from '@/utils/creator-delete'
import { syncViewRowFromFlatList } from '@/utils/view-dialog-refresh'
import { ElMessage, ElMessageBox } from 'element-plus'
import {
Plus,
@@ -371,9 +392,24 @@ import { useDictStore } from '@/store/modules/dict'
defineOptions({ name: 'SystemPost' })
const dictStore = useDictStore()
const listColumnDefs = [
{ prop: 'postCode', label: '职位编码' },
{ prop: 'status', label: '状态' },
{ prop: 'createTime', label: '创建时间' }
]
const {
visibleColumns,
checkAll,
isIndeterminate,
isColumnVisible,
resetToDefault
} = usePersistedListColumns('system-post-list-columns', listColumnDefs)
const dictStore = useDictStore()
const searchForm = reactive<{ keyword: string }>({ keyword: '' })
const quickSearchKeyword = ref('')
const tableData = ref<Api.Post.PostItem[]>([])
const selectedRows = ref<Api.Post.PostItem[]>([])
const loading = ref(false)
@@ -403,6 +439,11 @@ const dictStore = useDictStore()
// ---- 查看弹窗 ----
const viewVisible = ref(false)
const viewRow = ref<Api.Post.PostItem | null>(null)
function refreshViewIfOpen() {
if (!viewVisible.value) return
syncViewRowFromFlatList(viewRow, tableData.value)
}
const viewBoundUserNames = ref<string[]>([])
const viewSections = computed(() => {
const row = viewRow.value
@@ -636,10 +677,11 @@ const dictStore = useDictStore()
async function loadData() {
loading.value = true
try {
const quickKeyword = quickSearchKeyword.value?.trim()
const res = await fetchPostList({
page: pagination.page,
pageSize: pagination.pageSize,
keyword: searchForm.keyword || undefined
keyword: quickKeyword || searchForm.keyword || undefined
})
tableData.value = res.list || []
pagination.total = res.total ?? 0
@@ -649,12 +691,25 @@ const dictStore = useDictStore()
}
function handleSearch() {
quickSearchKeyword.value = ''
pagination.page = 1
loadData()
}
function handleReset() {
searchForm.keyword = ''
quickSearchKeyword.value = ''
pagination.page = 1
loadData()
}
function handleQuickSearch() {
pagination.page = 1
loadData()
}
function handleQuickSearchClear() {
quickSearchKeyword.value = ''
pagination.page = 1
loadData()
}
@@ -708,6 +763,7 @@ const dictStore = useDictStore()
ElMessage.success(isEdit.value ? '更新成功' : '创建成功')
dialogVisible.value = false
await loadData()
if (isEdit.value) refreshViewIfOpen()
} catch {
/* 校验或接口错误已由全局处理 */
} finally {
+62 -5
View File
@@ -33,6 +33,12 @@
<div class="list-toolbar-selected">
已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
</div>
<ListQuickSearchInput
v-model="quickSearchKeyword"
placeholder="角色名称"
@search="handleQuickSearch"
@clear="handleQuickSearchClear"
/>
<el-button class="btn-main" type="primary" @click="handleAdd">
<el-icon><Plus /></el-icon>
新增角色
@@ -82,7 +88,15 @@
批量删除
</el-button>
<ListToolbarEndActions :loading="loading" @refresh="loadData" />
<ListToolbarEndActions
:loading="loading"
:column-defs="listColumnDefs"
v-model:visible-columns="visibleColumns"
v-model:check-all="checkAll"
:is-indeterminate="isIndeterminate"
@refresh="loadData"
@reset-columns="resetToDefault"
/>
</div>
</div>
@@ -117,6 +131,7 @@
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('dataScope')"
prop="dataScope"
label="数据权限"
min-width="120"
@@ -128,6 +143,7 @@
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('status')"
prop="status"
label="状态"
min-width="100"
@@ -141,6 +157,7 @@
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('createTime')"
prop="createTime"
label="创建时间"
min-width="180"
@@ -177,8 +194,9 @@
ref="formRef"
:model="formData"
:rules="formRules"
label-position="top"
class="role-dialog-form"
label-position="right"
label-width="100px"
class="role-dialog-form sys-dialog-form"
scroll-to-error
:scroll-into-view-options="{ behavior: 'smooth', block: 'center' }"
>
@@ -258,7 +276,7 @@
destroy-on-close
:close-on-click-modal="false"
>
<el-form label-position="top" class="data-scope-form">
<el-form label-position="right" label-width="100px" class="data-scope-form sys-dialog-form">
<el-form-item label="数据权限范围">
<el-select v-model="dataScopeForm.dataScope" style="width: 100%">
<el-option label="全部数据" value="1" />
@@ -452,8 +470,11 @@
<script setup lang="ts">
import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'
import ListQuickSearchInput from '@/components/business/list-quick-search-input/index.vue'
import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
import { ref, reactive, computed, onMounted, nextTick, watch } from 'vue'
import { canDeleteByCreator, filterDeletableByCreator, allRowsDeletableByCreator, CREATOR_DELETE_TIP } from '@/utils/creator-delete'
import { syncViewRowFromFlatList } from '@/utils/view-dialog-refresh'
import { ElMessage, ElMessageBox } from 'element-plus'
import {
Plus,
@@ -492,12 +513,27 @@ import { useDictStore } from '@/store/modules/dict'
defineOptions({ name: 'SystemRole' })
const listColumnDefs = [
{ prop: 'dataScope', label: '数据权限' },
{ prop: 'status', label: '状态' },
{ prop: 'createTime', label: '创建时间' }
]
const {
visibleColumns,
checkAll,
isIndeterminate,
isColumnVisible,
resetToDefault
} = usePersistedListColumns('system-role-list-columns', listColumnDefs)
const dictStore = useDictStore()
const dataScopeLabel = (code: string) => dictStore.getLabel('sys_data_scope', code)
const dataScopeTagType = (code: string) => dictStore.getTagType('sys_data_scope', code)
const searchForm = reactive<{ keyword: string }>({ keyword: '' })
const quickSearchKeyword = ref('')
const tableData = ref<Api.Role.RoleItem[]>([])
const selectedRows = ref<Api.Role.RoleItem[]>([])
const loading = ref(false)
@@ -695,10 +731,11 @@ const dictStore = useDictStore()
async function loadData() {
loading.value = true
try {
const quickKeyword = quickSearchKeyword.value?.trim()
const res = await fetchRoleList({
page: pagination.page,
pageSize: pagination.pageSize,
keyword: searchForm.keyword || undefined
keyword: quickKeyword || searchForm.keyword || undefined
})
tableData.value = res.list || []
pagination.total = res.total ?? 0
@@ -708,12 +745,25 @@ const dictStore = useDictStore()
}
function handleSearch() {
quickSearchKeyword.value = ''
pagination.page = 1
loadData()
}
function handleReset() {
searchForm.keyword = ''
quickSearchKeyword.value = ''
pagination.page = 1
loadData()
}
function handleQuickSearch() {
pagination.page = 1
loadData()
}
function handleQuickSearchClear() {
quickSearchKeyword.value = ''
pagination.page = 1
loadData()
}
@@ -763,6 +813,7 @@ const dictStore = useDictStore()
}
dialogVisible.value = false
await loadData()
if (isEdit.value) await refreshViewIfOpen()
} catch {
/* handled globally */
} finally {
@@ -978,6 +1029,12 @@ const dictStore = useDictStore()
// ---- 查看弹窗 ----
const viewVisible = ref(false)
const viewRow = ref<Api.Role.RoleItem | null>(null)
async function refreshViewIfOpen() {
if (!viewVisible.value || !viewRow.value) return
syncViewRowFromFlatList(viewRow, tableData.value)
if (viewRow.value) await loadViewDetail(viewRow.value)
}
const viewDetailLoading = ref(false)
const viewDataScopeCode = ref('')
const viewMenuTreeData = ref<Api.Menu.MenuItem[]>([])
+3 -2
View File
@@ -197,8 +197,9 @@
ref="formRef"
:model="formData"
:rules="formRules"
label-position="top"
class="site-dialog-form"
label-position="right"
label-width="100px"
class="site-dialog-form sys-dialog-form"
scroll-to-error
:scroll-into-view-options="{ behavior: 'smooth', block: 'center' }"
>
+62 -4
View File
@@ -33,6 +33,12 @@
<div class="list-toolbar-selected">
已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
</div>
<ListQuickSearchInput
v-model="quickSearchKeyword"
placeholder="租户编号/名称"
@search="handleQuickSearch"
@clear="handleQuickSearchClear"
/>
<el-button class="btn-main" type="primary" @click="handleAdd">
<el-icon><Plus /></el-icon>
新增租户
@@ -76,7 +82,15 @@
批量删除
</el-button>
<ListToolbarEndActions :loading="loading" @refresh="loadData" />
<ListToolbarEndActions
:loading="loading"
:column-defs="listColumnDefs"
v-model:visible-columns="visibleColumns"
v-model:check-all="checkAll"
:is-indeterminate="isIndeterminate"
@refresh="loadData"
@reset-columns="resetToDefault"
/>
</div>
</div>
@@ -106,6 +120,7 @@
:index="(index) => (pagination.page - 1) * pagination.pageSize + index + 1"
/>
<el-table-column
v-if="isColumnVisible('tenantNo')"
prop="tenantNo"
label="租户编号"
min-width="120"
@@ -136,6 +151,7 @@
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('contactName')"
prop="contactName"
label="联系人"
min-width="100"
@@ -143,6 +159,7 @@
header-align="center"
/>
<el-table-column
v-if="isColumnVisible('contactPhone')"
prop="contactPhone"
label="联系电话"
min-width="140"
@@ -150,6 +167,7 @@
header-align="center"
/>
<el-table-column
v-if="isColumnVisible('createTime')"
prop="createTime"
label="创建时间"
min-width="180"
@@ -183,8 +201,9 @@
ref="formRef"
:model="formData"
:rules="formRules"
label-position="top"
class="tenant-dialog-form"
label-position="right"
label-width="100px"
class="tenant-dialog-form sys-dialog-form"
scroll-to-error
:scroll-into-view-options="{ behavior: 'smooth', block: 'center' }"
>
@@ -258,8 +277,11 @@
<script setup lang="ts">
import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'
import ListQuickSearchInput from '@/components/business/list-quick-search-input/index.vue'
import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
import { ref, reactive, onMounted, computed } from 'vue'
import { canDeleteByCreator, filterDeletableByCreator, allRowsDeletableByCreator, CREATOR_DELETE_TIP } from '@/utils/creator-delete'
import { syncViewRowFromFlatList } from '@/utils/view-dialog-refresh'
import { ElMessage, ElMessageBox } from 'element-plus'
import {
Plus,
@@ -285,7 +307,23 @@ import { canDeleteByCreator, filterDeletableByCreator, allRowsDeletableByCreator
defineOptions({ name: 'SystemTenant' })
const listColumnDefs = [
{ prop: 'tenantNo', label: '租户编号' },
{ prop: 'contactName', label: '联系人' },
{ prop: 'contactPhone', label: '联系电话' },
{ prop: 'createTime', label: '创建时间' }
]
const {
visibleColumns,
checkAll,
isIndeterminate,
isColumnVisible,
resetToDefault
} = usePersistedListColumns('system-tenant-list-columns', listColumnDefs)
const searchForm = reactive<{ keyword: string }>({ keyword: '' })
const quickSearchKeyword = ref('')
const tableData = ref<Api.Tenant.TenantItem[]>([])
const selectedRows = ref<Api.Tenant.TenantItem[]>([])
const loading = ref(false)
@@ -299,6 +337,11 @@ import { canDeleteByCreator, filterDeletableByCreator, allRowsDeletableByCreator
const viewVisible = ref(false)
const viewRow = ref<Api.Tenant.TenantItem | null>(null)
function refreshViewIfOpen() {
if (!viewVisible.value) return
syncViewRowFromFlatList(viewRow, tableData.value)
}
const formData = reactive({
tenantNo: '',
tenantName: '',
@@ -355,10 +398,11 @@ import { canDeleteByCreator, filterDeletableByCreator, allRowsDeletableByCreator
async function loadData() {
loading.value = true
try {
const quickKeyword = quickSearchKeyword.value?.trim()
const res = await fetchTenantList({
page: pagination.page,
pageSize: pagination.pageSize,
keyword: searchForm.keyword || undefined
keyword: quickKeyword || searchForm.keyword || undefined
})
tableData.value = res.list || []
pagination.total = res.total ?? 0
@@ -368,12 +412,25 @@ import { canDeleteByCreator, filterDeletableByCreator, allRowsDeletableByCreator
}
function handleSearch() {
quickSearchKeyword.value = ''
pagination.page = 1
loadData()
}
function handleReset() {
searchForm.keyword = ''
quickSearchKeyword.value = ''
pagination.page = 1
loadData()
}
function handleQuickSearch() {
pagination.page = 1
loadData()
}
function handleQuickSearchClear() {
quickSearchKeyword.value = ''
pagination.page = 1
loadData()
}
@@ -429,6 +486,7 @@ import { canDeleteByCreator, filterDeletableByCreator, allRowsDeletableByCreator
}
dialogVisible.value = false
await loadData()
if (isEdit.value) refreshViewIfOpen()
} catch {
/* 校验或接口错误已由全局处理 */
} finally {
+37 -3
View File
@@ -62,6 +62,12 @@
<div class="list-toolbar-selected">
已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
</div>
<ListQuickSearchInput
v-model="quickSearchKeyword"
placeholder="账号/姓名/手机号"
@search="handleQuickSearch"
@clear="handleQuickSearchClear"
/>
<el-button class="btn-main" type="primary" @click="handleAdd">
<el-icon><Plus /></el-icon>
新增用户
@@ -222,8 +228,9 @@
ref="formRef"
:model="formData"
:rules="formRules"
label-position="top"
class="user-dialog-form"
label-position="right"
label-width="100px"
class="user-dialog-form sys-dialog-form"
scroll-to-error
:scroll-into-view-options="{ behavior: 'smooth', block: 'center' }"
>
@@ -328,6 +335,7 @@
<script setup lang="ts">
import { ref, reactive, computed, onMounted, watch } from 'vue'
import { canDeleteByCreator, filterDeletableByCreator, allRowsDeletableByCreator, CREATOR_DELETE_TIP } from '@/utils/creator-delete'
import { syncViewRowFromFlatList } from '@/utils/view-dialog-refresh'
import { ElMessage, ElMessageBox } from 'element-plus'
import {
Plus,
@@ -342,6 +350,7 @@
} from '@element-plus/icons-vue'
import SystemViewDialog from '@/components/business/system-view-dialog/index.vue'
import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'
import ListQuickSearchInput from '@/components/business/list-quick-search-input/index.vue'
import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
import {
fetchUserList,
@@ -400,6 +409,7 @@ const dictStore = useDictStore()
}
const searchForm = reactive<{ keyword: string; status: string }>({ keyword: '', status: '' })
const quickSearchKeyword = ref('')
const tableData = ref<Api.User.UserItem[]>([])
const selectedRows = ref<Api.User.UserItem[]>([])
const loading = ref(false)
@@ -476,10 +486,11 @@ const dictStore = useDictStore()
async function loadData() {
loading.value = true
try {
const quickKeyword = quickSearchKeyword.value?.trim()
const res = await fetchUserList({
page: pagination.page,
pageSize: pagination.pageSize,
keyword: searchForm.keyword || undefined,
keyword: quickKeyword || searchForm.keyword || undefined,
deptId: selectedDeptId.value || undefined,
status: searchForm.status || undefined
})
@@ -491,6 +502,7 @@ const dictStore = useDictStore()
}
function handleSearch() {
quickSearchKeyword.value = ''
pagination.page = 1
loadData()
}
@@ -498,12 +510,24 @@ const dictStore = useDictStore()
function handleReset() {
searchForm.keyword = ''
searchForm.status = ''
quickSearchKeyword.value = ''
selectedDeptId.value = null
deptTreeRef.value?.setCurrentKey(null)
pagination.page = 1
loadData()
}
function handleQuickSearch() {
pagination.page = 1
loadData()
}
function handleQuickSearchClear() {
quickSearchKeyword.value = ''
pagination.page = 1
loadData()
}
function resetForm() {
formData.userNo = ''
formData.nickName = ''
@@ -590,6 +614,7 @@ const dictStore = useDictStore()
}
dialogVisible.value = false
await loadData()
if (isEdit.value) await refreshViewIfOpen()
} catch {
/* 校验或接口错误已由全局处理 */
} finally {
@@ -664,6 +689,15 @@ const dictStore = useDictStore()
const viewVisible = ref(false)
const viewRow = ref<Api.User.UserItem | null>(null)
async function refreshViewIfOpen() {
if (!viewVisible.value || !viewRow.value?.id) return
if (isEdit.value && editingId.value) {
viewRow.value = await fetchUserDetail(editingId.value)
return
}
syncViewRowFromFlatList(viewRow, tableData.value)
}
const viewSections = computed(() => {
const row = viewRow.value
if (!row) return []
@@ -10,8 +10,9 @@
ref="formRef"
:model="formData"
:rules="rules"
label-position="top"
class="user-form-grid"
label-position="right"
label-width="100px"
class="user-form-grid sys-dialog-form"
>
<ElFormItem label="用户名" prop="username">
<ElInput v-model="formData.username" placeholder="请输入用户名" />
@@ -138,7 +138,7 @@
删除节点
</el-button>
</div>
<el-form label-position="top" class="props-form">
<el-form label-position="right" label-width="100px" class="props-form wf-dialog-form">
<el-form-item label="节点名称">
<el-input
v-model="selectedNode.nodeName"
@@ -258,7 +258,7 @@
<span class="edge-to">{{ selectedEdge.toNodeName }}</span>
</div>
<el-form label-position="top" class="props-form" style="margin-top: 12px">
<el-form label-position="right" label-width="100px" class="props-form wf-dialog-form" style="margin-top: 12px">
<el-form-item>
<el-checkbox v-model="selectedEdge.isDefault" @change="syncEdgeCondition">
默认分支(其他分支都不满足时走此路径)
@@ -46,7 +46,21 @@
<div class="list-toolbar">
<div class="list-toolbar-row">
<ListToolbarEndActions :loading="loading" @refresh="loadData" />
<ListQuickSearchInput
v-model="quickSearchKeyword"
placeholder="流程名称/业务单号"
@search="handleQuickSearch"
@clear="handleQuickSearchClear"
/>
<ListToolbarEndActions
:loading="loading"
:column-defs="listColumnDefs"
v-model:visible-columns="visibleColumns"
v-model:check-all="checkAll"
:is-indeterminate="isIndeterminate"
@refresh="loadData"
@reset-columns="resetToDefault"
/>
</div>
</div>
@@ -58,7 +72,9 @@
stripe
row-key="id"
@row-dblclick="handleViewDetail"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" min-width="55" align="center" header-align="center" />
<el-table-column
type="index"
label="序号"
@@ -80,6 +96,7 @@
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('businessTypeLabel')"
prop="businessTypeLabel"
label="业务类型"
min-width="120"
@@ -87,6 +104,7 @@
header-align="center"
/>
<el-table-column
v-if="isColumnVisible('businessId')"
prop="businessId"
label="业务单号"
min-width="200"
@@ -95,6 +113,7 @@
show-overflow-tooltip
/>
<el-table-column
v-if="isColumnVisible('status')"
prop="status"
label="状态"
min-width="100"
@@ -106,6 +125,7 @@
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('createTime')"
prop="createTime"
label="发起时间"
min-width="180"
@@ -204,27 +224,41 @@
</div>
</template>
</div>
<template #footer>
<el-button class="btn-ghost" @click="detailVisible = false"
><el-icon><Close /></el-icon>关闭</el-button
>
</template>
</el-dialog>
</section>
</div>
</template>
<script setup lang="ts">
import ListQuickSearchInput from '@/components/business/list-quick-search-input/index.vue'
import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'
import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
import { fetchMyInstances, fetchBusinessAllTasks } from '@/api/workflow'
import ProcessStatusTag from '../components/ProcessStatusTag.vue'
import { Document, Search, RefreshLeft, Close } from '@element-plus/icons-vue'
defineOptions({ name: 'WorkflowMyApplications' })
const listColumnDefs = [
{ prop: 'businessTypeLabel', label: '业务类型' },
{ prop: 'businessId', label: '业务单号' },
{ prop: 'status', label: '状态' },
{ prop: 'createTime', label: '发起时间' }
]
const {
visibleColumns,
checkAll,
isIndeterminate,
isColumnVisible,
resetToDefault
} = usePersistedListColumns('workflow-my-applications-list-columns', listColumnDefs)
const searchForm = reactive<{ keyword: string; status: string }>({ keyword: '', status: '' })
const quickSearchKeyword = ref('')
const tableData = ref<Api.Workflow.MyInstanceItem[]>([])
const loading = ref(false)
const selectedRows = ref<Api.Workflow.MyInstanceItem[]>([])
const pagination = reactive({ page: 1, pageSize: 20, total: 0 })
const detailVisible = ref(false)
@@ -235,10 +269,11 @@
async function loadData() {
loading.value = true
try {
const quickKeyword = quickSearchKeyword.value?.trim()
const res = await fetchMyInstances({
page: pagination.page,
pageSize: pagination.pageSize,
keyword: searchForm.keyword || undefined,
keyword: quickKeyword || searchForm.keyword || undefined,
status: searchForm.status || undefined
})
tableData.value = res.list || []
@@ -249,6 +284,7 @@
}
function handleSearch() {
quickSearchKeyword.value = ''
pagination.page = 1
loadData()
}
@@ -256,10 +292,26 @@
function handleReset() {
searchForm.keyword = ''
searchForm.status = ''
quickSearchKeyword.value = ''
pagination.page = 1
loadData()
}
function handleQuickSearch() {
pagination.page = 1
loadData()
}
function handleQuickSearchClear() {
quickSearchKeyword.value = ''
pagination.page = 1
loadData()
}
function handleSelectionChange(rows: Api.Workflow.MyInstanceItem[]) {
selectedRows.value = rows
}
async function handleViewDetail(row: Api.Workflow.MyInstanceItem) {
detailVisible.value = true
detailLoading.value = true
@@ -285,6 +337,7 @@
watchDebounced(
searchForm,
() => {
quickSearchKeyword.value = ''
pagination.page = 1
loadData()
},
@@ -38,6 +38,12 @@
<div class="list-toolbar-selected">
已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
</div>
<ListQuickSearchInput
v-model="quickSearchKeyword"
placeholder="流程名称/编码"
@search="handleQuickSearch"
@clear="handleQuickSearchClear"
/>
<el-button class="btn-main" type="primary" @click="handleAdd">
<el-icon><Plus /></el-icon>
新增流程
@@ -79,7 +85,15 @@
批量删除
</el-button>
<ListToolbarEndActions :loading="loading" @refresh="loadData" />
<ListToolbarEndActions
:loading="loading"
:column-defs="listColumnDefs"
v-model:visible-columns="visibleColumns"
v-model:check-all="checkAll"
:is-indeterminate="isIndeterminate"
@refresh="loadData"
@reset-columns="resetToDefault"
/>
</div>
</div>
@@ -103,6 +117,7 @@
:index="(index) => (pagination.page - 1) * pagination.pageSize + index + 1"
/>
<el-table-column
v-if="isColumnVisible('processCode')"
prop="processCode"
label="流程编码"
min-width="150"
@@ -121,7 +136,13 @@
<el-link type="primary" @click="handleView(row)">{{ row.processName }}</el-link>
</template>
</el-table-column>
<el-table-column label="状态" min-width="100" align="center" header-align="center">
<el-table-column
v-if="isColumnVisible('status')"
label="状态"
min-width="100"
align="center"
header-align="center"
>
<template #default="{ row }">
<el-switch
:model-value="row.status"
@@ -131,12 +152,19 @@
/>
</template>
</el-table-column>
<el-table-column label="最新版本" min-width="100" align="center" header-align="center">
<el-table-column
v-if="isColumnVisible('latestVersion')"
label="最新版本"
min-width="100"
align="center"
header-align="center"
>
<template #default="{ row }">
{{ row.latestVersion != null ? `V${row.latestVersion}` : '--' }}
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('createTime')"
prop="createTime"
label="创建时间"
min-width="160"
@@ -172,7 +200,9 @@
ref="formRef"
:model="formData"
:rules="formRules"
label-position="top"
label-position="right"
label-width="100px"
class="wf-dialog-form"
scroll-to-error
:scroll-into-view-options="{ behavior: 'smooth', block: 'center' }"
>
@@ -242,13 +272,16 @@
</template>
<script setup lang="ts">
import ListQuickSearchInput from '@/components/business/list-quick-search-input/index.vue'
import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'
import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
import {
canDeleteByCreator,
filterDeletableByCreator,
allRowsDeletableByCreator,
CREATOR_DELETE_TIP
} from '@/utils/creator-delete'
import { syncViewRowFromFlatList } from '@/utils/view-dialog-refresh'
import {
Plus,
Close,
@@ -274,7 +307,23 @@ import ListToolbarEndActions from '@/components/business/list-toolbar-end-action
defineOptions({ name: 'WorkflowProcess' })
const listColumnDefs = [
{ prop: 'processCode', label: '流程编码' },
{ prop: 'status', label: '状态' },
{ prop: 'latestVersion', label: '最新版本' },
{ prop: 'createTime', label: '创建时间' }
]
const {
visibleColumns,
checkAll,
isIndeterminate,
isColumnVisible,
resetToDefault
} = usePersistedListColumns('workflow-process-list-columns', listColumnDefs)
const searchForm = reactive<{ keyword: string }>({ keyword: '' })
const quickSearchKeyword = ref('')
const tableData = ref<Api.Workflow.ProcessDefItem[]>([])
const selectedRows = ref<Api.Workflow.ProcessDefItem[]>([])
const loading = ref(false)
@@ -309,6 +358,11 @@ import ListToolbarEndActions from '@/components/business/list-toolbar-end-action
const viewVisible = ref(false)
const viewingRecord = ref<Api.Workflow.ProcessDefItem | null>(null)
function refreshViewIfOpen() {
if (!viewVisible.value) return
syncViewRowFromFlatList(viewingRecord, tableData.value)
}
const processViewSections = computed<ViewSection[]>(() => {
const row = viewingRecord.value
if (!row) return []
@@ -337,10 +391,11 @@ import ListToolbarEndActions from '@/components/business/list-toolbar-end-action
async function loadData() {
loading.value = true
try {
const quickKeyword = quickSearchKeyword.value?.trim()
const res = await fetchProcessDefList({
page: pagination.page,
pageSize: pagination.pageSize,
keyword: searchForm.keyword || undefined
keyword: quickKeyword || searchForm.keyword || undefined
})
tableData.value = res.list || []
pagination.total = res.total ?? 0
@@ -350,12 +405,25 @@ import ListToolbarEndActions from '@/components/business/list-toolbar-end-action
}
function handleSearch() {
quickSearchKeyword.value = ''
pagination.page = 1
loadData()
}
function handleReset() {
searchForm.keyword = ''
quickSearchKeyword.value = ''
pagination.page = 1
loadData()
}
function handleQuickSearch() {
pagination.page = 1
loadData()
}
function handleQuickSearchClear() {
quickSearchKeyword.value = ''
pagination.page = 1
loadData()
}
@@ -405,6 +473,7 @@ import ListToolbarEndActions from '@/components/business/list-toolbar-end-action
}
dialogVisible.value = false
await loadData()
if (isEdit.value) refreshViewIfOpen()
} catch {
// 校验或接口错误已由全局处理
} finally {
@@ -502,6 +571,7 @@ import ListToolbarEndActions from '@/components/business/list-toolbar-end-action
watchDebounced(
searchForm,
() => {
quickSearchKeyword.value = ''
pagination.page = 1
loadData()
},
@@ -34,7 +34,21 @@
<div class="list-toolbar">
<div class="list-toolbar-row">
<ListToolbarEndActions :loading="loading" @refresh="loadData" />
<ListQuickSearchInput
v-model="quickSearchKeyword"
placeholder="流程名称"
@search="handleQuickSearch"
@clear="handleQuickSearchClear"
/>
<ListToolbarEndActions
:loading="loading"
:column-defs="listColumnDefs"
v-model:visible-columns="visibleColumns"
v-model:check-all="checkAll"
:is-indeterminate="isIndeterminate"
@refresh="loadData"
@reset-columns="resetToDefault"
/>
</div>
</div>
@@ -46,7 +60,9 @@
stripe
row-key="id"
@row-dblclick="handleViewDetail"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" min-width="55" align="center" header-align="center" />
<el-table-column
type="index"
label="序号"
@@ -67,6 +83,7 @@
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('businessTypeLabel')"
prop="businessTypeLabel"
label="业务类型"
min-width="120"
@@ -74,6 +91,7 @@
header-align="center"
/>
<el-table-column
v-if="isColumnVisible('businessId')"
prop="businessId"
label="业务单号"
min-width="200"
@@ -82,18 +100,26 @@
show-overflow-tooltip
/>
<el-table-column
v-if="isColumnVisible('starterUserName')"
prop="starterUserName"
label="发起人"
min-width="100"
align="center"
header-align="center"
/>
<el-table-column label="审批结果" min-width="100" align="center" header-align="center">
<el-table-column
v-if="isColumnVisible('status')"
label="审批结果"
min-width="100"
align="center"
header-align="center"
>
<template #default="{ row }">
<ProcessStatusTag :status="row.status || ''" />
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('commentText')"
prop="commentText"
label="审批意见"
min-width="150"
@@ -102,6 +128,7 @@
show-overflow-tooltip
/>
<el-table-column
v-if="isColumnVisible('updateTime')"
prop="updateTime"
label="审批时间"
min-width="180"
@@ -202,27 +229,43 @@
</div>
</template>
</div>
<template #footer>
<el-button class="btn-ghost" @click="detailVisible = false"
><el-icon><Close /></el-icon>关闭</el-button
>
</template>
</el-dialog>
</section>
</div>
</template>
<script setup lang="ts">
import ListQuickSearchInput from '@/components/business/list-quick-search-input/index.vue'
import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'
import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
import { fetchCompletedTasks, fetchInstanceDetail } from '@/api/workflow'
import ProcessStatusTag from '../components/ProcessStatusTag.vue'
import { Document, Search, RefreshLeft, Close } from '@element-plus/icons-vue'
defineOptions({ name: 'WorkflowTaskCompleted' })
const listColumnDefs = [
{ prop: 'businessTypeLabel', label: '业务类型' },
{ prop: 'businessId', label: '业务单号' },
{ prop: 'starterUserName', label: '发起人' },
{ prop: 'status', label: '审批结果' },
{ prop: 'commentText', label: '审批意见', defaultVisible: false },
{ prop: 'updateTime', label: '审批时间' }
]
const {
visibleColumns,
checkAll,
isIndeterminate,
isColumnVisible,
resetToDefault
} = usePersistedListColumns('workflow-task-completed-list-columns', listColumnDefs)
const searchForm = reactive<{ keyword: string }>({ keyword: '' })
const quickSearchKeyword = ref('')
const tableData = ref<Api.Workflow.TaskItem[]>([])
const loading = ref(false)
const selectedRows = ref<Api.Workflow.TaskItem[]>([])
const pagination = reactive({ page: 1, pageSize: 20, total: 0 })
// 详情
@@ -233,10 +276,11 @@
async function loadData() {
loading.value = true
try {
const quickKeyword = quickSearchKeyword.value?.trim()
const res = await fetchCompletedTasks({
page: pagination.page,
pageSize: pagination.pageSize,
keyword: searchForm.keyword || undefined
keyword: quickKeyword || searchForm.keyword || undefined
})
tableData.value = res.list || []
pagination.total = res.total ?? 0
@@ -246,16 +290,33 @@
}
function handleSearch() {
quickSearchKeyword.value = ''
pagination.page = 1
loadData()
}
function handleReset() {
searchForm.keyword = ''
quickSearchKeyword.value = ''
pagination.page = 1
loadData()
}
function handleQuickSearch() {
pagination.page = 1
loadData()
}
function handleQuickSearchClear() {
quickSearchKeyword.value = ''
pagination.page = 1
loadData()
}
function handleSelectionChange(rows: Api.Workflow.TaskItem[]) {
selectedRows.value = rows
}
async function handleViewDetail(row: Api.Workflow.TaskItem) {
detailVisible.value = true
detailLoading.value = true
@@ -295,6 +356,7 @@
watchDebounced(
searchForm,
() => {
quickSearchKeyword.value = ''
pagination.page = 1
loadData()
},
@@ -37,6 +37,12 @@
<div class="list-toolbar-selected">
已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
</div>
<ListQuickSearchInput
v-model="quickSearchKeyword"
placeholder="流程名称"
@search="handleQuickSearch"
@clear="handleQuickSearchClear"
/>
<el-button
class="btn-main"
type="primary"
@@ -47,7 +53,15 @@
审批
</el-button>
<ListToolbarEndActions :loading="loading" @refresh="loadData" />
<ListToolbarEndActions
:loading="loading"
:column-defs="listColumnDefs"
v-model:visible-columns="visibleColumns"
v-model:check-all="checkAll"
:is-indeterminate="isIndeterminate"
@refresh="loadData"
@reset-columns="resetToDefault"
/>
</div>
</div>
@@ -83,6 +97,7 @@
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('businessTypeLabel')"
prop="businessTypeLabel"
label="业务类型"
min-width="120"
@@ -90,6 +105,7 @@
header-align="center"
/>
<el-table-column
v-if="isColumnVisible('businessId')"
prop="businessId"
label="业务单号"
min-width="200"
@@ -98,6 +114,7 @@
show-overflow-tooltip
/>
<el-table-column
v-if="isColumnVisible('starterUserName')"
prop="starterUserName"
label="发起人"
min-width="100"
@@ -105,6 +122,7 @@
header-align="center"
/>
<el-table-column
v-if="isColumnVisible('createTime')"
prop="createTime"
label="到达时间"
min-width="180"
@@ -219,18 +237,15 @@
</div>
</template>
</div>
<template #footer>
<el-button class="btn-ghost" @click="detailVisible = false"
><el-icon><Close /></el-icon>关闭</el-button
>
</template>
</el-dialog>
</section>
</div>
</template>
<script setup lang="ts">
import ListQuickSearchInput from '@/components/business/list-quick-search-input/index.vue'
import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'
import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
import { fetchPendingTasks, fetchInstanceDetail } from '@/api/workflow'
import ApprovalDialog from '../components/ApprovalDialog.vue'
import ProcessStatusTag from '../components/ProcessStatusTag.vue'
@@ -238,7 +253,23 @@ import ListToolbarEndActions from '@/components/business/list-toolbar-end-action
defineOptions({ name: 'WorkflowTaskPending' })
const listColumnDefs = [
{ prop: 'businessTypeLabel', label: '业务类型' },
{ prop: 'businessId', label: '业务单号' },
{ prop: 'starterUserName', label: '发起人' },
{ prop: 'createTime', label: '到达时间' }
]
const {
visibleColumns,
checkAll,
isIndeterminate,
isColumnVisible,
resetToDefault
} = usePersistedListColumns('workflow-task-pending-list-columns', listColumnDefs)
const searchForm = reactive<{ keyword: string }>({ keyword: '' })
const quickSearchKeyword = ref('')
const tableData = ref<Api.Workflow.TaskItem[]>([])
const selectedRows = ref<Api.Workflow.TaskItem[]>([])
const loading = ref(false)
@@ -262,10 +293,11 @@ import ListToolbarEndActions from '@/components/business/list-toolbar-end-action
async function loadData() {
loading.value = true
try {
const quickKeyword = quickSearchKeyword.value?.trim()
const res = await fetchPendingTasks({
page: pagination.page,
pageSize: pagination.pageSize,
keyword: searchForm.keyword || undefined
keyword: quickKeyword || searchForm.keyword || undefined
})
tableData.value = res.list || []
pagination.total = res.total ?? 0
@@ -275,12 +307,25 @@ import ListToolbarEndActions from '@/components/business/list-toolbar-end-action
}
function handleSearch() {
quickSearchKeyword.value = ''
pagination.page = 1
loadData()
}
function handleReset() {
searchForm.keyword = ''
quickSearchKeyword.value = ''
pagination.page = 1
loadData()
}
function handleQuickSearch() {
pagination.page = 1
loadData()
}
function handleQuickSearchClear() {
quickSearchKeyword.value = ''
pagination.page = 1
loadData()
}
@@ -299,8 +344,11 @@ import ListToolbarEndActions from '@/components/business/list-toolbar-end-action
approvalVisible.value = true
}
function handleApprovalDone() {
loadData()
async function handleApprovalDone() {
await loadData()
if (detailVisible.value && viewingRow.value) {
await handleViewDetail(viewingRow.value)
}
}
async function handleViewDetail(row: Api.Workflow.TaskItem) {
@@ -343,6 +391,7 @@ import ListToolbarEndActions from '@/components/business/list-toolbar-end-action
watchDebounced(
searchForm,
() => {
quickSearchKeyword.value = ''
pagination.page = 1
loadData()
},