提交“

This commit is contained in:
zlwu committed 2026-05-30 14:03:11 +08:00
1 parent 69ee0f505c
commit 9b88581450
614 files changed
+90318 -689022

No files matched your search

File diff suppressed because it is too large. Load diff
+7 -4
View File
@@ -83,8 +83,8 @@
header-align="center"
>
<template #default="{ row }">
<el-tag :type="row.status === '1' ? 'success' : 'danger'" size="small">
{{ row.status === '1' ? '正常' : '停用' }}
<el-tag :type="dictStore.getTagType('sys_status', row.status)" size="small">
{{ dictStore.getLabel('sys_status', row.status) }}
</el-tag>
</template>
</el-table-column>
@@ -278,9 +278,12 @@
fetchDictDataOptions,
fetchUserList
} from '@/api/admin'
import { buildTreePathLabelFromFlat } from '@/utils/tree-path'
import { buildTreePathLabelFromFlat } from '@/utils/tree-path'
import { useDictStore } from '@/store/modules/dict'
defineOptions({ name: 'SystemDept' })
defineOptions({ name: 'SystemDept' })
const dictStore = useDictStore()
const searchForm = reactive<{ keyword: string }>({ keyword: '' })
const tableData = ref<Api.Dept.DeptItem[]>([])
+62
View File
@@ -224,6 +224,24 @@
align="center"
header-align="center"
/>
<el-table-column
prop="tagType"
label="标签颜色"
min-width="100"
align="center"
header-align="center"
>
<template #default="{ row }">
<el-tag
v-if="row.tagType"
:type="row.tagType || undefined"
size="small"
>
{{ row.tagType === 'success' ? '成功' : row.tagType === 'warning' ? '警告' : row.tagType === 'danger' ? '危险' : row.tagType === 'info' ? '信息' : row.tagType === 'primary' ? '主要' : '默认' }}
</el-tag>
<span v-else class="text-gray-400">默认</span>
</template>
</el-table-column>
<el-table-column
prop="remark"
label="备注"
@@ -291,6 +309,24 @@
align="center"
header-align="center"
/>
<el-table-column
prop="tagType"
label="标签颜色"
min-width="100"
align="center"
header-align="center"
>
<template #default="{ row }">
<el-tag
v-if="row.tagType"
:type="row.tagType || undefined"
size="small"
>
{{ row.tagType === 'success' ? '成功' : row.tagType === 'warning' ? '警告' : row.tagType === 'danger' ? '危险' : row.tagType === 'info' ? '信息' : row.tagType === 'primary' ? '主要' : '默认' }}
</el-tag>
<span v-else class="text-gray-400">默认</span>
</template>
</el-table-column>
<el-table-column
prop="remark"
label="备注"
@@ -359,6 +395,28 @@
<el-form-item label="数据值" prop="dictValue">
<el-input v-model="dictDataFormData.dictValue" placeholder="数据值" />
</el-form-item>
<el-form-item label="标签颜色" prop="tagType">
<el-select v-model="dictDataFormData.tagType" placeholder="请选择" clearable>
<el-option label="默认" value="">
<el-tag size="small">预览</el-tag>
</el-option>
<el-option label="成功(绿)" value="success">
<el-tag type="success" size="small">成功</el-tag>
</el-option>
<el-option label="警告(橙)" value="warning">
<el-tag type="warning" size="small">警告</el-tag>
</el-option>
<el-option label="危险(红)" value="danger">
<el-tag type="danger" size="small">危险</el-tag>
</el-option>
<el-option label="信息(灰)" value="info">
<el-tag type="info" size="small">信息</el-tag>
</el-option>
<el-option label="主要(蓝)" value="primary">
<el-tag type="primary" size="small">主要</el-tag>
</el-option>
</el-select>
</el-form-item>
<el-form-item label="备注" style="grid-column: 1 / -1">
<el-input
v-model="dictDataFormData.remark"
@@ -617,6 +675,7 @@
const dictDataFormData = reactive({
dictLabel: '',
dictValue: '',
tagType: '',
orderNum: 0,
remark: ''
})
@@ -652,6 +711,7 @@
function resetDictDataForm() {
dictDataFormData.dictLabel = ''
dictDataFormData.dictValue = ''
dictDataFormData.tagType = ''
dictDataFormData.orderNum = 0
dictDataFormData.remark = ''
dictDataEditingId.value = null
@@ -671,6 +731,7 @@
dictDataEditingId.value = row.id
dictDataFormData.dictLabel = row.dictLabel
dictDataFormData.dictValue = row.dictValue
dictDataFormData.tagType = row.tagType || ''
dictDataFormData.orderNum = row.orderNum ?? 0
dictDataFormData.remark = row.remark || ''
dictDataFormVisible.value = true
@@ -684,6 +745,7 @@
const payload = {
dictLabel: dictDataFormData.dictLabel.trim(),
dictValue: dictDataFormData.dictValue.trim(),
tagType: dictDataFormData.tagType || undefined,
dictType: currentDictType.dictType,
orderNum: dictDataFormData.orderNum,
remark: dictDataFormData.remark?.trim() || undefined
@@ -1,388 +0,0 @@
<!-- 快递公司管理 -->
<!-- eslint-disable vue/no-v-model-argument -->
<template>
<div class="page-container">
<section class="panel">
<div class="panel-top">
<h2>快递公司列表</h2>
</div>
<div class="filter-block">
<el-form :model="searchForm" class="filter-form" @submit.prevent>
<div class="filter-grid">
<el-form-item label="关键词">
<el-input v-model="searchForm.keyword" placeholder="快递公司名称/编码" clearable />
</el-form-item>
</div>
<div class="filter-actions-row">
<span class="filter-hint">按快递公司名称、编码模糊查询。</span>
<div class="filter-actions-end">
<el-button class="btn-ghost" @click="handleReset"
><el-icon><RefreshLeft /></el-icon>重置</el-button
>
<el-button class="btn-main" type="primary" @click="handleSearch"
><el-icon><Search /></el-icon>搜索</el-button
>
</div>
</div>
</el-form>
</div>
<div class="list-toolbar">
<div class="list-toolbar-row">
<div class="list-toolbar-selected">
已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
</div>
<el-button class="btn-main" type="primary" @click="handleAdd">
<el-icon><Plus /></el-icon>
新增快递公司
</el-button>
<el-button
class="btn-ghost"
type="danger"
:disabled="selectedRows.length === 0"
@click="handleBatchDelete"
>
批量删除
</el-button>
</div>
</div>
<div class="list-body list-wrap">
<el-table
class="list-table"
:data="tableData"
v-loading="loading"
stripe
row-key="id"
@selection-change="handleSelectionChange"
@row-dblclick="handleView"
>
<el-table-column type="selection" min-width="55" align="center" header-align="center" />
<el-table-column
type="index"
label="序号"
min-width="68"
align="center"
header-align="center"
:index="(index) => (pagination.page - 1) * pagination.pageSize + index + 1"
/>
<el-table-column
prop="expressCode"
label="快递编码"
min-width="120"
align="center"
header-align="center"
/>
<el-table-column
prop="expressName"
label="快递名称"
min-width="140"
align="center"
header-align="center"
/>
<el-table-column
prop="createTime"
label="创建时间"
min-width="180"
align="center"
header-align="center"
/>
<el-table-column
label="操作"
min-width="120"
fixed="right"
align="center"
header-align="center"
>
<template #default="{ row }">
<div style="display: flex; justify-content: center; gap: 8px">
<el-button class="btn-op-item" @click="handleView(row)">
<el-icon><View /></el-icon>查看
</el-button>
<el-dropdown trigger="hover" @command="(cmd: string) => handleDropdownCommand(cmd, row)">
<el-button class="btn-op-item">
<el-icon><More /></el-icon>
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="edit">
<el-icon><Edit /></el-icon>编辑
</el-dropdown-item>
<el-dropdown-item command="delete" divided>
<el-icon><Delete /></el-icon>删除
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</template>
</el-table-column>
</el-table>
<div class="pagination list-pager-bar">
<el-pagination
:current-page="pagination.page"
:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50]"
layout="total, sizes, prev, pager, next"
@size-change="(val) => ((pagination.page = 1), (pagination.pageSize = val), loadData())"
@current-change="(val) => ((pagination.page = val), loadData())"
/>
</div>
</div>
<el-dialog
v-model="dialogVisible"
:title="dialogTitle"
width="560px"
class="dialog-form-layout"
align-center
destroy-on-close
:close-on-click-modal="false"
>
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-position="top"
class="express-dialog-form"
scroll-to-error
:scroll-into-view-options="{ behavior: 'smooth', block: 'center' }"
>
<div class="form-grid-2">
<el-form-item label="快递编码" prop="expressCode">
<el-input v-model="formData.expressCode" placeholder="请输入快递编码" />
</el-form-item>
<el-form-item label="快递名称" prop="expressName">
<el-input v-model="formData.expressName" placeholder="请输入快递名称" />
</el-form-item>
<el-form-item label="备注">
<el-input v-model="formData.remark" placeholder="可选" />
</el-form-item>
</div>
</el-form>
<template #footer>
<el-button class="btn-ghost" @click="dialogVisible = false">
<el-icon><Close /></el-icon>
取消
</el-button>
<el-button class="btn-main" type="primary" @click="handleSubmit" :loading="submitLoading">
<el-icon><Check /></el-icon>
确定
</el-button>
</template>
</el-dialog>
<SystemViewDialog
v-model:visible="viewVisible"
:title="viewRow?.expressName || '快递公司详情'"
:icon="Van"
:meta="viewRow?.createTime || ''"
:sections="viewSections"
/>
</section>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted, computed } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Plus, Close, Check, View, Van, RefreshLeft, Search, More, Edit, Delete } from '@element-plus/icons-vue'
import SystemViewDialog from '@/components/business/system-view-dialog/index.vue'
import { withFullscreenLoading, fullscreenLoadingText } from '@/utils/fullscreen-loading'
import {
fetchExpressCompanyList,
fetchCreateExpressCompany,
fetchUpdateExpressCompany,
fetchDeleteExpressCompany
} from '@/api/admin'
defineOptions({ name: 'SystemExpress' })
const searchForm = reactive<{ keyword: string }>({ keyword: '' })
const tableData = ref<Api.ExpressCompany.ExpressCompanyItem[]>([])
const selectedRows = ref<Api.ExpressCompany.ExpressCompanyItem[]>([])
const loading = ref(false)
const pagination = reactive({ page: 1, pageSize: 10, total: 0 })
const dialogVisible = ref(false)
const dialogTitle = ref('新增快递公司')
const isEdit = ref(false)
const submitLoading = ref(false)
const formRef = ref()
const editingId = ref<string | null>(null)
const viewVisible = ref(false)
const viewRow = ref<Api.ExpressCompany.ExpressCompanyItem | null>(null)
const formData = reactive({
expressCode: '',
expressName: '',
orderNum: 0,
remark: ''
})
const formRules = {
expressCode: [{ required: true, message: '请输入快递编码', trigger: 'blur' }],
expressName: [{ required: true, message: '请输入快递名称', trigger: 'blur' }]
}
const handleSelectionChange = (rows: Api.ExpressCompany.ExpressCompanyItem[]) => {
selectedRows.value = rows
}
const viewSections = computed(() => {
if (!viewRow.value) return []
const row = viewRow.value
return [
{
title: '基本信息',
fields: [
{ label: '快递编码', value: row.expressCode },
{ label: '快递名称', value: row.expressName },
{ label: '备注', value: row.remark, span: true }
]
}
]
})
async function loadData() {
loading.value = true
try {
const res = await fetchExpressCompanyList({
page: pagination.page,
pageSize: pagination.pageSize,
keyword: searchForm.keyword || undefined
})
tableData.value = res.list || []
pagination.total = res.total ?? 0
} finally {
loading.value = false
}
}
function handleSearch() {
pagination.page = 1
loadData()
}
function handleReset() {
searchForm.keyword = ''
pagination.page = 1
loadData()
}
function resetForm() {
formData.expressCode = ''
formData.expressName = ''
formData.orderNum = 0
formData.remark = ''
editingId.value = null
}
function handleAdd() {
isEdit.value = false
dialogTitle.value = '新增快递公司'
resetForm()
dialogVisible.value = true
formRef.value?.clearValidate?.()
}
function handleEdit(row: Api.ExpressCompany.ExpressCompanyItem) {
isEdit.value = true
dialogTitle.value = '编辑快递公司'
editingId.value = row.id
formData.expressCode = row.expressCode || ''
formData.expressName = row.expressName || ''
formData.orderNum = row.orderNum ?? 0
formData.remark = row.remark || ''
dialogVisible.value = true
formRef.value?.clearValidate?.()
}
async function handleSubmit() {
await formRef.value?.validate?.()
submitLoading.value = true
try {
const payload = {
expressCode: formData.expressCode.trim(),
expressName: formData.expressName.trim(),
orderNum: formData.orderNum,
remark: formData.remark?.trim() || undefined
}
if (isEdit.value && editingId.value) {
await fetchUpdateExpressCompany(editingId.value, payload)
} else {
await fetchCreateExpressCompany(payload)
}
ElMessage.success(isEdit.value ? '更新成功' : '创建成功')
dialogVisible.value = false
await loadData()
} catch {
/* 校验或接口错误已由全局处理 */
} finally {
submitLoading.value = false
}
}
function handleView(row: Api.ExpressCompany.ExpressCompanyItem) {
viewRow.value = row
viewVisible.value = true
}
function handleDropdownCommand(command: string, row: any) {
switch (command) {
case 'edit':
handleEdit(row)
break
case 'delete':
handleDelete(row)
break
}
}
async function handleDelete(row: Api.ExpressCompany.ExpressCompanyItem) {
await ElMessageBox.confirm(`确定删除快递公司「${row.expressName}」吗?`, '提示', {
type: 'warning'
})
await fetchDeleteExpressCompany(row.id)
ElMessage.success('删除成功')
await loadData()
}
async function handleBatchDelete() {
const ids = selectedRows.value.map((r) => r.id)
if (!ids.length) return
await ElMessageBox.confirm(`确定删除选中的 ${ids.length} 个快递公司吗?`, '提示', {
type: 'warning'
})
await withFullscreenLoading(async () => {
for (const id of ids) {
await fetchDeleteExpressCompany(id)
}
ElMessage.success('批量删除成功')
selectedRows.value = []
await loadData()
}, fullscreenLoadingText.batchDelete)
}
// 输入检索条件时自动搜索(防抖 300ms)
watchDebounced(
searchForm,
() => {
pagination.page = 1
loadData()
},
{ debounce: 300, deep: true }
)
onMounted(() => {
loadData()
})
</script>
<style scoped lang="scss">
.express-dialog-form {
:deep(.el-form-item) {
margin-bottom: 0;
}
}
</style>
+5 -6
View File
@@ -96,8 +96,8 @@
header-align="center"
>
<template #default="{ row }">
<el-tag :type="statusTagType(row.status)" size="small">{{
statusLabel(row.status)
<el-tag :type="dictStore.getTagType('leave_status', row.status)" size="small">{{
dictStore.getLabel('leave_status', row.status)
}}</el-tag>
</template>
</el-table-column>
@@ -236,7 +236,7 @@
<script setup lang="ts">
import { Close, Check, RefreshLeft, Search } from '@element-plus/icons-vue'
import { leaveStatusLabel, leaveStatusTagType } from '@/utils/status-tag-registry'
import { useDictStore } from '@/store/modules/dict'
import {
fetchLeaveList,
fetchCreateLeave,
@@ -247,6 +247,8 @@
defineOptions({ name: 'LeaveRequest' })
const dictStore = useDictStore()
const loading = ref(false)
const tableData = ref<Api.Leave.LeaveItem[]>([])
const selectedRows = ref<Api.Leave.LeaveItem[]>([])
@@ -268,9 +270,6 @@
remark: ''
})
const statusLabel = leaveStatusLabel
const statusTagType = leaveStatusTagType
function resetForm() {
formData.leaveType = 'personal'
formData.startDate = ''
+15 -18
View File
@@ -71,8 +71,8 @@
header-align="center"
>
<template #default="{ row }">
<el-tag :type="menuTypeTagMap[row.menuType]?.type || 'info'" size="small">
{{ menuTypeTagMap[row.menuType]?.label || row.menuType }}
<el-tag :type="dictStore.getTagType('menu_type', row.menuType)" size="small">
{{ dictStore.getLabel('menu_type', row.menuType) }}
</el-tag>
</template>
</el-table-column>
@@ -117,8 +117,8 @@
header-align="center"
>
<template #default="{ row }">
<el-tag :type="row.visible === '1' ? 'success' : 'info'" size="small">
{{ row.visible === '1' ? '显示' : '隐藏' }}
<el-tag :type="dictStore.getTagType('sys_visible', row.visible)" size="small">
{{ dictStore.getLabel('sys_visible', row.visible) }}
</el-tag>
</template>
</el-table-column>
@@ -130,8 +130,8 @@
header-align="center"
>
<template #default="{ row }">
<el-tag :type="row.status === '1' ? 'success' : 'danger'" size="small">
{{ row.status === '1' ? '正常' : '停用' }}
<el-tag :type="dictStore.getTagType('sys_status', row.status)" size="small">
{{ dictStore.getLabel('sys_status', row.status) }}
</el-tag>
</template>
</el-table-column>
@@ -256,7 +256,7 @@
v-model:visible="viewVisible"
:title="viewRow?.menuName || '菜单详情'"
:icon="MenuIcon"
:meta="`类型:${menuTypeTagMap[viewRow?.menuType || '']?.label || viewRow?.menuType || ''}${viewRow?.createTime ? ' · ' + viewRow.createTime : ''}`"
:meta="`类型:${dictStore.getLabel('menu_type', viewRow?.menuType || '')}${viewRow?.createTime ? ' · ' + viewRow.createTime : ''}`"
:sections="viewSections"
/>
</section>
@@ -280,14 +280,11 @@
} from '@element-plus/icons-vue'
import SystemViewDialog from '@/components/business/system-view-dialog/index.vue'
import { fetchMenuTree, fetchCreateMenu, fetchUpdateMenu, fetchDeleteMenu } from '@/api/admin'
import { useDictStore } from '@/store/modules/dict'
defineOptions({ name: 'SystemMenu' })
const menuTypeTagMap: Record<string, { label: string; type: string }> = {
M: { label: '目录', type: '' },
C: { label: '菜单', type: 'success' },
F: { label: '按钮', type: 'warning' }
}
const dictStore = useDictStore()
const searchForm = reactive<{ keyword: string }>({ keyword: '' })
const tableData = ref<Api.Menu.MenuItem[]>([])
@@ -347,8 +344,8 @@
{ label: '菜单名称', value: viewRow.value?.menuName },
{
label: '菜单类型',
value: menuTypeTagMap[viewRow.value?.menuType || '']?.label || viewRow.value?.menuType,
tagType: 'primary'
value: dictStore.getLabel('menu_type', viewRow.value?.menuType || ''),
tagType: dictStore.getTagType('menu_type', viewRow.value?.menuType || '')
},
{ label: '路由路径', value: viewRow.value?.path },
{ label: '组件路径', value: viewRow.value?.component },
@@ -356,13 +353,13 @@
{ label: '是否外链', value: viewRow.value?.isFrame === 1 ? '否' : '是' },
{
label: '显示状态',
value: viewRow.value?.visible === '1' ? '显示' : '隐藏',
tagType: viewRow.value?.visible === '1' ? 'success' : 'info'
value: dictStore.getLabel('sys_visible', viewRow.value?.visible || ''),
tagType: dictStore.getTagType('sys_visible', viewRow.value?.visible || '')
},
{
label: '状态',
value: viewRow.value?.status === '1' ? '正常' : '停用',
tagType: viewRow.value?.status === '1' ? 'success' : 'info'
value: dictStore.getLabel('sys_status', viewRow.value?.status || ''),
tagType: dictStore.getTagType('sys_status', viewRow.value?.status || '')
},
{ label: '备注', value: viewRow.value?.remark, span: true }
]
File diff suppressed because it is too large. Load diff
+7 -4
View File
@@ -89,8 +89,8 @@
header-align="center"
>
<template #default="{ row }">
<el-tag :type="row.status === '1' ? 'success' : 'info'" size="small">
{{ row.status === '1' ? '正常' : '停用' }}
<el-tag :type="dictStore.getTagType('sys_status', row.status)" size="small">
{{ dictStore.getLabel('sys_status', row.status) }}
</el-tag>
</template>
</el-table-column>
@@ -335,6 +335,7 @@
import SystemViewDialog from '@/components/business/system-view-dialog/index.vue'
import { MAX_ADMIN_LIST_PAGE_SIZE } from '@/utils/fetch-paged-list'
import { withFullscreenLoading, fullscreenLoadingText } from '@/utils/fullscreen-loading'
import { useDictStore } from '@/store/modules/dict'
import {
fetchPostList,
fetchCreatePost,
@@ -349,6 +350,8 @@
defineOptions({ name: 'SystemPost' })
const dictStore = useDictStore()
const searchForm = reactive<{ keyword: string }>({ keyword: '' })
const tableData = ref<Api.Post.PostItem[]>([])
const selectedRows = ref<Api.Post.PostItem[]>([])
@@ -391,8 +394,8 @@
{ label: '职位编码', value: row.postCode },
{
label: '状态',
value: row.status === '1' ? '正常' : '停用',
tagType: row.status === '1' ? 'success' : 'info'
value: dictStore.getLabel('sys_status', row.status),
tagType: dictStore.getTagType('sys_status', row.status)
},
{ label: '创建时间', value: row.createTime },
{ label: '备注', value: row.remark, span: true }
File diff suppressed because it is too large. Load diff
+10 -6
View File
@@ -93,8 +93,8 @@
header-align="center"
>
<template #default="{ row }">
<el-tag :type="row.status === '1' ? 'success' : 'info'" size="small">
{{ row.status === '1' ? '正常' : '停用' }}
<el-tag :type="dictStore.getTagType('sys_status', row.status)" size="small">
{{ dictStore.getLabel('sys_status', row.status) }}
</el-tag>
</template>
</el-table-column>
@@ -443,7 +443,8 @@
import { MAX_ADMIN_LIST_PAGE_SIZE } from '@/utils/fetch-paged-list'
import { withFullscreenLoading, fullscreenLoadingText } from '@/utils/fullscreen-loading'
import { buildPathLabelMapFromTreeRoots } from '@/utils/tree-path'
import { dataScopeLabel as dataScopeLabelFn, dataScopeTagType } from '@/utils/status-tag-registry'
import { useDictStore } from '@/store/modules/dict'
import {
fetchRoleList,
fetchCreateRole,
@@ -460,7 +461,10 @@
defineOptions({ name: 'SystemRole' })
const dataScopeLabel = dataScopeLabelFn
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 tableData = ref<Api.Role.RoleItem[]>([])
@@ -949,8 +953,8 @@
{ label: '角色名称', value: row.roleName },
{
label: '状态',
value: row.status === '1' ? '正常' : '停用',
tagType: row.status === '1' ? ('success' as const) : ('info' as const)
value: dictStore.getLabel('sys_status', row.status),
tagType: dictStore.getTagType('sys_status', row.status) as 'success' | 'info'
},
{ label: '创建时间', value: row.createTime },
{ label: '备注', value: row.remark, span: true }
+7 -4
View File
@@ -100,8 +100,8 @@
header-align="center"
>
<template #default="{ row }">
<el-tag :type="row.status === '1' ? 'success' : 'info'" size="small">
{{ row.status === '1' ? '正常' : '停用' }}
<el-tag :type="dictStore.getTagType('sys_status', row.status)" size="small">
{{ dictStore.getLabel('sys_status', row.status) }}
</el-tag>
</template>
</el-table-column>
@@ -302,6 +302,7 @@
import { ElMessage, ElMessageBox } from 'element-plus'
import { Plus, Close, Check, View, Edit, Delete, More, Location, RefreshLeft, Search } from '@element-plus/icons-vue'
import { withFullscreenLoading, fullscreenLoadingText } from '@/utils/fullscreen-loading'
import { useDictStore } from '@/store/modules/dict'
import SystemViewDialog from '@/components/business/system-view-dialog/index.vue'
import {
fetchSiteList,
@@ -314,6 +315,8 @@
defineOptions({ name: 'SystemSite' })
const dictStore = useDictStore()
// ---- 查看弹窗 ----
const viewVisible = ref(false)
const viewRow = ref<Api.Site.SiteItem | null>(null)
@@ -329,8 +332,8 @@
{ label: '站点类型', value: getSiteTypeLabel(row.siteType), tagType: 'primary' },
{
label: '状态',
value: row.status === '1' ? '正常' : '停用',
tagType: row.status === '1' ? 'success' : 'info'
value: dictStore.getLabel('sys_status', row.status),
tagType: dictStore.getTagType('sys_status', row.status)
},
{ label: '地址', value: row.address, span: true }
]
+1 -1
View File
@@ -252,7 +252,7 @@
} from '@/api/admin'
/** 与后端 `settings.DEFAULT_TENANT_NO` 一致 */
const DEFAULT_TENANT_NO = 'zkxw'
const DEFAULT_TENANT_NO = 'shanxia2.0'
defineOptions({ name: 'SystemTenant' })
-397
View File
@@ -1,397 +0,0 @@
<!-- 物品单位管理 -->
<!-- eslint-disable vue/no-v-model-argument -->
<template>
<div class="page-container">
<section class="panel">
<div class="panel-top">
<h2>单位列表</h2>
</div>
<div class="filter-block">
<el-form :model="searchForm" class="filter-form" @submit.prevent>
<div class="filter-grid">
<el-form-item label="关键词">
<el-input v-model="searchForm.keyword" placeholder="单位名称/编码" clearable />
</el-form-item>
</div>
<div class="filter-actions-row">
<span class="filter-hint">按名称、编码模糊查询。</span>
<div class="filter-actions-end">
<el-button class="btn-ghost" @click="handleReset"
><el-icon><RefreshLeft /></el-icon>重置</el-button
>
<el-button class="btn-main" type="primary" @click="handleSearch"
><el-icon><Search /></el-icon>搜索</el-button
>
</div>
</div>
</el-form>
</div>
<div class="list-toolbar">
<div class="list-toolbar-row">
<div class="list-toolbar-selected">
已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
</div>
<el-button class="btn-main" type="primary" @click="handleAdd">
<el-icon><Plus /></el-icon>
新增单位
</el-button>
<el-button
class="btn-ghost"
type="danger"
:disabled="selectedRows.length === 0"
@click="handleBatchDelete"
>
批量删除
</el-button>
</div>
</div>
<div class="list-body list-wrap">
<el-table
class="list-table"
:data="tableData"
v-loading="loading"
stripe
row-key="id"
@selection-change="handleSelectionChange"
@row-dblclick="handleView"
>
<el-table-column type="selection" min-width="55" align="center" header-align="center" />
<el-table-column
type="index"
label="序号"
min-width="68"
align="center"
header-align="center"
:index="(index) => (pagination.page - 1) * pagination.pageSize + index + 1"
/>
<el-table-column
prop="unitName"
label="单位名称"
min-width="150"
align="center"
header-align="center"
/>
<el-table-column
prop="unitCode"
label="单位编码"
min-width="120"
align="center"
header-align="center"
/>
<el-table-column
prop="createTime"
label="创建时间"
min-width="180"
align="center"
header-align="center"
/>
<el-table-column
label="操作"
min-width="120"
fixed="right"
align="center"
header-align="center"
>
<template #default="{ row }">
<div style="display: flex; justify-content: center; gap: 8px">
<el-button class="btn-op-item" @click="handleView(row)">
<el-icon><View /></el-icon>查看
</el-button>
<el-dropdown trigger="hover" @command="(cmd: string) => handleDropdownCommand(cmd, row)">
<el-button class="btn-op-item">
<el-icon><More /></el-icon>
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="edit">
<el-icon><Edit /></el-icon>编辑
</el-dropdown-item>
<el-dropdown-item command="delete" divided>
<el-icon><Delete /></el-icon>删除
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</template>
</el-table-column>
</el-table>
<div class="pagination list-pager-bar">
<el-pagination
:current-page="pagination.page"
:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50]"
layout="total, sizes, prev, pager, next"
@size-change="(val) => ((pagination.page = 1), (pagination.pageSize = val), loadData())"
@current-change="(val) => ((pagination.page = val), loadData())"
/>
</div>
</div>
<el-dialog
v-model="dialogVisible"
:title="dialogTitle"
width="560px"
class="dialog-form-layout"
align-center
destroy-on-close
:close-on-click-modal="false"
>
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-position="top"
class="unit-dialog-form"
scroll-to-error
:scroll-into-view-options="{ behavior: 'smooth', block: 'center' }"
>
<div class="form-grid-2">
<el-form-item label="单位名称" prop="unitName">
<el-input v-model="formData.unitName" placeholder="如:斤、kg、个" />
</el-form-item>
<el-form-item label="单位编码" prop="unitCode">
<el-input v-model="formData.unitCode" placeholder="如:jin、kg、ge" />
</el-form-item>
<el-form-item label="备注">
<el-input v-model="formData.remark" placeholder="可选" />
</el-form-item>
</div>
</el-form>
<template #footer>
<el-button class="btn-ghost" @click="dialogVisible = false">
<el-icon><Close /></el-icon>
取消
</el-button>
<el-button class="btn-main" type="primary" @click="handleSubmit" :loading="submitLoading">
<el-icon><Check /></el-icon>
确定
</el-button>
</template>
</el-dialog>
<SystemViewDialog
v-model:visible="viewVisible"
:title="viewRow?.unitName || '单位详情'"
:icon="ScaleToOriginal"
:meta="viewRow?.createTime || ''"
:sections="viewSections"
/>
</section>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted, computed } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import {
Plus,
Close,
Check,
View,
Edit,
Delete,
More,
ScaleToOriginal,
RefreshLeft,
Search
} from '@element-plus/icons-vue'
import SystemViewDialog from '@/components/business/system-view-dialog/index.vue'
import { withFullscreenLoading, fullscreenLoadingText } from '@/utils/fullscreen-loading'
import {
fetchItemUnitList,
fetchCreateItemUnit,
fetchUpdateItemUnit,
fetchDeleteItemUnit
} from '@/api/admin'
defineOptions({ name: 'SystemItemUnit' })
const searchForm = reactive<{ keyword: string }>({ keyword: '' })
const tableData = ref<Api.ItemUnit.ItemUnitItem[]>([])
const selectedRows = ref<Api.ItemUnit.ItemUnitItem[]>([])
const loading = ref(false)
const pagination = reactive({ page: 1, pageSize: 10, total: 0 })
const dialogVisible = ref(false)
const dialogTitle = ref('新增单位')
const isEdit = ref(false)
const submitLoading = ref(false)
const formRef = ref()
const editingId = ref<string | null>(null)
const viewVisible = ref(false)
const viewRow = ref<Api.ItemUnit.ItemUnitItem | null>(null)
const formData = reactive({
unitName: '',
unitCode: '',
orderNum: 0,
remark: ''
})
const formRules = {
unitName: [{ required: true, message: '请输入单位名称', trigger: 'blur' }],
unitCode: [{ required: true, message: '请输入单位编码', trigger: 'blur' }]
}
const handleSelectionChange = (rows: Api.ItemUnit.ItemUnitItem[]) => {
selectedRows.value = rows
}
const viewSections = computed(() => {
if (!viewRow.value) return []
const row = viewRow.value
return [
{
title: '基本信息',
fields: [
{ label: '单位名称', value: row.unitName },
{ label: '单位编码', value: row.unitCode },
{ label: '备注', value: row.remark, span: true }
]
}
]
})
async function loadData() {
loading.value = true
try {
const res = await fetchItemUnitList({
page: pagination.page,
pageSize: pagination.pageSize,
keyword: searchForm.keyword || undefined
})
tableData.value = res.list || []
pagination.total = res.total ?? 0
} finally {
loading.value = false
}
}
function handleSearch() {
pagination.page = 1
loadData()
}
function handleReset() {
searchForm.keyword = ''
pagination.page = 1
loadData()
}
function resetForm() {
formData.unitName = ''
formData.unitCode = ''
formData.orderNum = 0
formData.remark = ''
editingId.value = null
}
function handleAdd() {
isEdit.value = false
dialogTitle.value = '新增单位'
resetForm()
dialogVisible.value = true
formRef.value?.clearValidate?.()
}
function handleEdit(row: Api.ItemUnit.ItemUnitItem) {
isEdit.value = true
dialogTitle.value = '编辑单位'
editingId.value = row.id
formData.unitName = row.unitName || ''
formData.unitCode = row.unitCode || ''
formData.orderNum = row.orderNum ?? 0
formData.remark = row.remark || ''
dialogVisible.value = true
formRef.value?.clearValidate?.()
}
async function handleSubmit() {
await formRef.value?.validate?.()
submitLoading.value = true
try {
const payload = {
unitName: formData.unitName.trim(),
unitCode: formData.unitCode.trim(),
orderNum: formData.orderNum,
remark: formData.remark?.trim() || undefined
}
if (isEdit.value && editingId.value) {
await fetchUpdateItemUnit(editingId.value, payload)
} else {
await fetchCreateItemUnit(payload)
}
ElMessage.success(isEdit.value ? '更新成功' : '创建成功')
dialogVisible.value = false
await loadData()
} catch {
/* 校验或接口错误已由全局处理 */
} finally {
submitLoading.value = false
}
}
function handleView(row: Api.ItemUnit.ItemUnitItem) {
viewRow.value = row
viewVisible.value = true
}
function handleDropdownCommand(command: string, row: any) {
switch (command) {
case 'edit':
handleEdit(row)
break
case 'delete':
handleDelete(row)
break
}
}
async function handleDelete(row: Api.ItemUnit.ItemUnitItem) {
await ElMessageBox.confirm('确定要删除该单位吗?', '提示', { type: 'warning' })
await fetchDeleteItemUnit(row.id)
ElMessage.success('删除成功')
await loadData()
}
async function handleBatchDelete() {
const ids = selectedRows.value.map((r) => r.id)
if (!ids.length) return
await ElMessageBox.confirm(`确定删除选中的 ${ids.length} 个单位吗?`, '提示', {
type: 'warning'
})
await withFullscreenLoading(async () => {
for (const id of ids) {
await fetchDeleteItemUnit(id)
}
ElMessage.success('批量删除成功')
selectedRows.value = []
await loadData()
}, fullscreenLoadingText.batchDelete)
}
// 输入检索条件时自动搜索(防抖 300ms)
watchDebounced(
searchForm,
() => {
pagination.page = 1
loadData()
},
{ debounce: 300, deep: true }
)
onMounted(() => {
loadData()
})
</script>
<style scoped lang="scss">
.unit-dialog-form {
:deep(.el-form-item) {
margin-bottom: 0;
}
}
</style>
+11 -29
View File
@@ -34,11 +34,11 @@
<div class="profile-info">
<div class="profile-name">{{ userData.realName }}</div>
<div class="profile-tags">
<el-tag :type="getRoleTagType(userData.role)" size="small">{{
getRoleText(userData.role)
<el-tag :type="dictStore.getTagType('user_role', userData.role)" size="small">{{
dictStore.getLabel('user_role', userData.role)
}}</el-tag>
<el-tag :type="userData.isActive ? 'success' : 'danger'" size="small">{{
userData.isActive ? '启用' : '禁用'
<el-tag :type="dictStore.getTagType('sys_status', userData.isActive ? '1' : '0')" size="small">{{
dictStore.getLabel('sys_status', userData.isActive ? '1' : '0')
}}</el-tag>
<el-tag v-if="userData.regionName" size="small">{{ userData.regionName }}</el-tag>
</div>
@@ -61,7 +61,7 @@
</div>
<div class="info-item">
<div class="info-label">角色</div>
<div class="info-value">{{ getRoleText(userData.role) }}</div>
<div class="info-value">{{ dictStore.getLabel('user_role', userData.role) }}</div>
</div>
<div class="info-item">
<div class="info-label">所属区域</div>
@@ -299,13 +299,16 @@
import { ElMessage, ElMessageBox } from 'element-plus'
import { withFullscreenLoading, fullscreenLoadingText } from '@/utils/fullscreen-loading'
import { ArrowLeft, Close, Check } from '@element-plus/icons-vue'
import { fetchUserDetail, fetchUpdateUser, fetchActiveRegions } from '@/api/admin'
import { fetchUserDetail, fetchUpdateUser } from '@/api/admin'
import { resolveUserAvatarUrl } from '@/utils/user-avatar'
import { formatMoneyDisplay } from '@/utils/money-format'
import { useDictStore } from '@/store/modules/dict'
import type { FormInstance, FormRules } from 'element-plus'
defineOptions({ name: 'UserDetail' })
const dictStore = useDictStore()
const router = useRouter()
const route = useRoute()
@@ -397,27 +400,6 @@
return userData.value.realName?.charAt(0) || '?'
})
// 获取角色相关
const getRoleTagType = (role: string) => {
const typeMap: Record<string, string> = {
admin: 'primary',
supplier: 'warning',
client: 'success',
deliver: 'info'
}
return typeMap[role] || 'info'
}
const getRoleText = (role: string) => {
const textMap: Record<string, string> = {
admin: '运营管理员',
supplier: '供应商',
client: '客户',
deliver: '配送员'
}
return textMap[role] || role
}
// 格式化金额
const formatAmount = (amount: number | string) => {
if (amount == null || amount === '') return '0.00'
@@ -518,8 +500,8 @@
// 加载区域
const loadRegions = async () => {
try {
const res = await fetchActiveRegions()
regionList.value = res
// TODO: 恢复区域加载(需要重新实现 region API)
regionList.value = []
} catch (error) {
console.error('加载区域失败:', error)
}
+8 -15
View File
@@ -165,8 +165,8 @@
header-align="center"
>
<template #default="{ row }">
<el-tag :type="row.status === '1' ? 'success' : 'info'" size="small">
{{ row.status === '1' ? '正常' : '停用' }}
<el-tag :type="dictStore.getTagType('sys_status', row.status)" size="small">
{{ dictStore.getLabel('sys_status', row.status) }}
</el-tag>
</template>
</el-table-column>
@@ -318,19 +318,12 @@
fetchDeptTree,
fetchRoleOptions
} from '@/api/admin'
import { buildPathLabelMapFromTreeRoots } from '@/utils/tree-path'
import { buildPathLabelMapFromTreeRoots } from '@/utils/tree-path'
import { useDictStore } from '@/store/modules/dict'
const roleOptions = ref<Array<{ label: string; value: string }>>([])
defineOptions({ name: 'SystemUser' })
async function loadRoleOptions() {
try {
roleOptions.value = (await fetchRoleOptions()) || []
} catch {
roleOptions.value = []
}
}
defineOptions({ name: 'SystemUser' })
const dictStore = useDictStore()
const deptKeyword = ref('')
const deptTree = ref<Api.Dept.DeptItem[]>([])
@@ -624,8 +617,8 @@
},
{
label: '状态',
value: row.status === '1' ? '正常' : '停用',
tagType: row.status === '1' ? 'success' : 'info'
value: dictStore.getLabel('sys_status', row.status),
tagType: dictStore.getTagType('sys_status', row.status)
},
{ label: '角色', value: formatUserRoleDisplay(row), span: true },
{ label: '创建时间', value: row.createTime },
@@ -4,22 +4,16 @@
</template>
<script setup lang="ts">
import { useDictStore } from '@/store/modules/dict'
defineOptions({ name: 'ProcessStatusTag' })
const dictStore = useDictStore()
const props = defineProps<{
status: string
}>()
const statusMap: Record<string, { type: string; label: string }> = {
running: { type: 'warning', label: '进行中' },
completed: { type: 'success', label: '已完成' },
rejected: { type: 'danger', label: '已驳回' },
cancelled: { type: 'info', label: '已取消' },
pending: { type: 'warning', label: '待处理' },
approved: { type: 'success', label: '已通过' },
withdrawn: { type: 'info', label: '已撤回' }
}
const tagType = computed(() => statusMap[props.status]?.type ?? 'info')
const label = computed(() => statusMap[props.status]?.label ?? props.status)
const tagType = computed(() => dictStore.getTagType('wf_process_status', props.status))
const label = computed(() => dictStore.getLabel('wf_process_status', props.status))
</script>