基础设置主要分为以下几个模块:
这边菜单是写死的,因为只是WMS需要的不多。
1、主页面代码
<template>
<div class="container">
<div>
<!-- Main Content -->
<v-card class="mt-5">
<v-card-text>
<div class="operateArea">
<v-row no-gutters>
<!-- Operate Btn -->
<v-col cols="12" sm="3" class="col">
<tooltip-btn icon="mdi-plus" :tooltip-text="$t('system.page.add')" @click="method.add()"></tooltip-btn>
<tooltip-btn icon="mdi-pencil-outline" :tooltip-text="$t('system.page.edit')" @click="method.editForm()"></tooltip-btn>
<tooltip-btn icon="mdi-delete-outline" :tooltip-text="$t('system.page.delete')" @click="method.deleteForm()"></tooltip-btn>
<tooltip-btn icon="mdi-refresh" :tooltip-text="$t('system.page.refresh')" @click="method.refresh()"></tooltip-btn>
<!-- <tooltip-btn icon="mdi-export-variant" :tooltip-text="$t('system.page.export')" @click="method.exportTable"></tooltip-btn> -->
</v-col>
<!-- Search Input -->
<v-col cols="12" sm="9">
<!-- <v-row no-gutters @keyup.enter="method.sureSearch">
<v-col cols="12" sm="4">
<v-text-field
v-model="data.searchForm.userName"
clearable
hide-details
density="comfortable"
class="searchInput ml-5 mt-1"
:label="$t('login.userName')"
variant="solo"
>
</v-text-field>
</v-col>
<v-col cols="12" sm="4">
<v-text-field
v-model="data.searchForm.userName1"
clearable
hide-details
density="comfortable"
class="searchInput ml-5 mt-1"
:label="$t('login.userName')"
variant="solo"
>
</v-text-field>
</v-col>
<v-col cols="12" sm="4">
<v-text-field
v-model="data.searchForm.userName2"
clearable
hide-details
density="comfortable"
class="searchInput ml-5 mt-1"
:label="$t('login.userName')"
variant="solo"
>
</v-text-field>
</v-col>
</v-row> -->
</v-col>
</v-row>
</div>
<!-- Table -->
<div
class="mt-5"
:style="{
height: cardHeight
}"
>
<v-row :no-gutters="true">
<v-col :cols="3" class="dataListCol">
<v-card :height="tableHeight">
<NavListVue
:list-data="data.roleList"
:title="data.navListOptions.title"
:label-key="data.navListOptions.labelKey"
:index-key="data.navListOptions.indexKey"
:index-value="data.navListOptions.indexValue"
@item-click="method.navListClick"
/>
</v-card>
</v-col>
<v-col :cols="9">
<vxe-table ref="xTable" :data="data.activeRoleMenuForm.detailList" :height="tableHeight" align="center">
<template #empty>
{{ i18n.global.t('system.page.noData') }}
</template>
<vxe-column type="seq" width="60"></vxe-column>
<vxe-column field="menu_name" :title="$t('base.roleMenu.menu_name')">
<template #default="{ row }">
<span>{{ $t(`router.sideBar.${row.menu_name}`) }}</span>
</template>
</vxe-column>
</vxe-table>
</v-col>
</v-row>
</div>
</v-card-text>
</v-card>
</div>
<!-- Add or modify data mode window -->
<addOrUpdateDialog :show-dialog="data.showDialog" :form="data.dialogForm" @close="method.closeDialog" @saveSuccess="method.saveSuccess" />
</div>
</template>
<script lang="ts" setup>
import { computed, reactive, onMounted, ref } from 'vue'
import { computedCardHeight, computedTableHeight } from '@/constant/style'
import tooltipBtn from '@/components/tooltip-btn.vue'
import { DataProps, RoleMenuVO } from '@/types/Base/RoleMenu'
import { getRoleMenuAll, getRoleMenuById, deleteRoleMenu } from '@/api/base/roleMenu'
import { hookComponent } from '@/components/system'
import addOrUpdateDialog from './add-or-update-role-menu.vue'
import i18n from '@/languages/i18n'
import NavListVue from '@/components/page/nav-list.vue'
import { exportData } from '@/utils/exportTable'
const xTable = ref()
const data: DataProps = reactive({
navListOptions: {
title: i18n.global.t('base.roleMenu.role_name'),
labelKey: 'role_name',
indexKey: 'userrole_id',
indexValue: ''
},
// Activation id
activeRoleMenuForm: {
userrole_id: 0,
role_name: '',
detailList: []
},
// searchForm: {
// userName: '',
// userName1: '',
// userName2: ''
// },
roleList: [],
// Dialog info
showDialog: false,
dialogForm: {
detailList: []
}
})
const method = reactive({
sureSearch: () => {
// console.log(data.searchForm)
},
// Find Data by Pagination
getCompanyList: async () => {
// Clear detailed data before refreshing
method.clearDialogForm()
const { data: res } = await getRoleMenuAll()
if (!res.isSuccess) {
hookComponent.$message({
type: 'error',
content: res.errorMessage
})
return
}
data.roleList = res.data
if (data.roleList.findIndex((item) => item.userrole_id === data.activeRoleMenuForm.userrole_id) > -1 && data.activeRoleMenuForm.userrole_id) {
method.getRoleMenus(data.activeRoleMenuForm.userrole_id)
} else if (data.roleList.length > 0) {
method.navListClick(data.roleList[0])
} else {
data.activeRoleMenuForm = {
userrole_id: 0,
role_name: '',
detailList: []
}
}
},
// Add user
add: () => {
method.clearDialogForm()
data.showDialog = true
},
// Shut add or update dialog
closeDialog: () => {
data.showDialog = false
},
// after Add or update success.
saveSuccess: () => {
method.refresh()
method.closeDialog()
},
// Refresh data
refresh: () => {
method.getCompanyList()
},
editForm() {
if (!data.activeRoleMenuForm.userrole_id) {
hookComponent.$message({
type: 'error',
content: i18n.global.t('base.roleMenu.beforeUpdateOrDel')
})
return
}
data.dialogForm = JSON.parse(JSON.stringify(data.activeRoleMenuForm))
// Delete rowid of existing data
for (const item of data.dialogForm.detailList) {
if (item._X_ROW_KEY) {
delete item._X_ROW_KEY
}
}
data.showDialog = true
},
deleteForm() {
if (!data.activeRoleMenuForm.userrole_id) {
hookComponent.$message({
type: 'error',
content: i18n.global.t('base.roleMenu.beforeUpdateOrDel')
})
return
}
const row = data.activeRoleMenuForm
hookComponent.$dialog({
content: i18n.global.t('system.tips.beforeDeleteMessage'),
handleConfirm: async () => {
if (row.userrole_id) {
const { data: res } = await deleteRoleMenu(row.userrole_id)
if (!res.isSuccess) {
hookComponent.$message({
type: 'error',
content: res.errorMessage
})
return
}
hookComponent.$message({
type: 'success',
content: `${ i18n.global.t('system.page.delete') }${ i18n.global.t('system.tips.success') }`
})
method.refresh()
}
}
})
},
// Export table
exportTable: () => {
const $table = xTable.value
exportData({
table: $table,
filename: i18n.global.t('router.sideBar.roleMenu'),
columnFilterMethod({ column }: any) {
return !['checkbox'].includes(column?.type) && !['operate'].includes(column?.field)
}
})
},
// Get detailed data
getRoleMenus: async (userrole_id: number) => {
const { data: res } = await getRoleMenuById(userrole_id)
if (!res.isSuccess) {
hookComponent.$message({
type: 'error',
content: res.errorMessage
})
return
}
data.activeRoleMenuForm = res.data
},
// Refresh dialog data
clearDialogForm: () => {
data.dialogForm = {
detailList: []
}
},
// Click navList
navListClick: (item: RoleMenuVO) => {
if (!item.userrole_id) {
return
}
data.navListOptions.indexValue = String(item.userrole_id)
method.getRoleMenus(item.userrole_id)
}
})
onMounted(async () => {
await method.getCompanyList()
})
const cardHeight = computed(() => computedCardHeight({ hasTab: false }))
const tableHeight = computed(() => computedTableHeight({ hasTab: false, hasPager: false }))
</script>
<style scoped lang="less">
.operateArea {
width: 100%;
min-width: 760px;
display: flex;
align-items: center;
border-radius: 10px;
padding: 0 10px;
}
.col {
display: flex;
align-items: center;
}
// Adjust the spacing between lists and tables
.dataListCol {
box-sizing: border-box;
padding-right: 10px !important;
}
.roleNameCol {
width: 100%;
height: 100%;
cursor: pointer;
// &:hover: {
// background-color: #9156fd;
// }
}
.roleNameCol:hover {
background-color: #9156fd;
color: white;
}
.activeRow {
background-color: #9156fd;
}
</style>
2、窗体代码
<template>
<v-dialog v-model="isShow" :width="'30%'" transition="dialog-top-transition" :persistent="true">
<template #default>
<v-card class="formCard">
<v-toolbar color="white" :title="`${$t('router.sideBar.roleMenu')}`"></v-toolbar>
<v-card-text>
<v-form ref="formRef">
<v-select
v-model="data.form.userrole_id"
:items="data.combobox.role_name"
item-title="label"
item-value="value"
:rules="data.rules.role_name"
:label="$t('base.roleMenu.role_name')"
variant="outlined"
clearable
></v-select>
<v-select
v-model="data.menusSelectedList"
:items="data.combobox.menu_name"
:menu-props="{ maxHeight: 400 }"
item-title="label"
item-value="value"
:label="$t('base.roleMenu.menu_name')"
variant="outlined"
chips
multiple
clearable
>
</v-select>
</v-form>
</v-card-text>
<v-card-actions class="justify-end">
<v-btn variant="text" @click="method.closeDialog">{{ $t('system.page.close') }}</v-btn>
<v-btn color="primary" variant="text" @click="method.submit">{{ $t('system.page.submit') }}</v-btn>
</v-card-actions>
</v-card>
</template>
</v-dialog>
</template>
<script lang="ts" setup>
import { reactive, computed, ref, watch } from 'vue'
import { RoleMenuVO, RoleMenuDetailVo } from '@/types/Base/RoleMenu'
import { UserRoleVO } from '@/types/Base/UserRoleSetting'
import i18n from '@/languages/i18n'
import { hookComponent } from '@/components/system/index'
import { addRoleMenu, updateRoleMenu, getMenus } from '@/api/base/roleMenu'
import { getUserRoleAll } from '@/api/base/userRoleSetting'
import { MenuItem } from '@/types/System/Store'
const formRef = ref()
const emit = defineEmits(['close', 'saveSuccess'])
const props = defineProps<{
showDialog: boolean
form: RoleMenuVO
}>()
const isShow = computed(() => props.showDialog)
const data = reactive({
dialogTitle: '',
form: ref<RoleMenuVO>({
userrole_id: 0,
role_name: '',
detailList: []
}),
// Multiple drop-down box values, mainly used to temporarily save the selected values
menusSelectedList: ref<any[]>([]),
// If it is modified, you need to pass a negative ID to the API to delete the existing details
removeDetailList: ref<RoleMenuDetailVo[]>([]),
// Drop down box options
combobox: ref<{
role_name: {
label: string
value: number
}[]
menu_name: {
label: string
value: number
}[]
}>({
role_name: [],
menu_name: []
}),
rules: {
role_name: [(val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.roleMenu.role_name') }!`],
menu_name: [(val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.roleMenu.menu_name') }!`]
}
})
const method = reactive({
// Get dialog type, add or update
getDialogType: () => {
// This is special because this document is not actually a table,
// but the data associated with another table.
// so directly use the details to determine whether it is a new document
if (props.form.detailList.length > 0) {
data.dialogTitle = 'update'
} else {
data.dialogTitle = 'add'
}
},
// Get the options required by the drop-down box
getCombobox: async () => {
data.combobox.role_name = []
const { data: res } = await getUserRoleAll()
if (!res.isSuccess) {
return
}
const roleNameList: UserRoleVO[] = res.data
for (const role of roleNameList) {
data.combobox.role_name.push({
label: role.role_name,
value: role.id
})
}
data.combobox.menu_name = []
const { data: menuRes } = await getMenus()
if (!menuRes.isSuccess) {
return
}
const menus: MenuItem[] = menuRes.data
for (const menu of menus) {
data.combobox.menu_name.push({
label: i18n.global.t(`router.sideBar.${ menu.menu_name }`),
value: menu.id
})
}
},
closeDialog: () => {
emit('close')
},
// Details verification before document submission
beforeSubmit: (): boolean => {
let flag = true
// Process the new dropdown options
for (const item of data.menusSelectedList) {
if (data.form.detailList.findIndex((dl) => dl.menu_id === item) < 0) {
data.form.detailList.push({
id: 0,
menu_id: item
})
}
}
if (data.dialogTitle === 'update') {
for (const item of data.form.detailList) {
if (!data.menusSelectedList.includes(item.menu_id)) {
// Delete if ID is negative
item.id = -item.id
}
}
}
if (data.form.detailList.length === 0) {
hookComponent.$message({
type: 'error',
content: i18n.global.t('system.tips.detailLengthIsZero')
})
flag = false
}
return flag
},
submit: async () => {
const beforeSubmitFlag = method.beforeSubmit()
if (!beforeSubmitFlag) {
return
}
const { valid } = await formRef.value.validate()
if (valid) {
const { data: res } = data.dialogTitle === 'add'
? await addRoleMenu(data.form)
: await updateRoleMenu({ ...data.form, detailList: [...data.form.detailList, ...data.removeDetailList] }) // Merge the deleted list and the original list
if (!res.isSuccess) {
hookComponent.$message({
type: 'error',
content: res.errorMessage
})
return
}
hookComponent.$message({
type: 'success',
content: `${ i18n.global.t('system.page.submit') }${ i18n.global.t('system.tips.success') }`
})
emit('saveSuccess')
} else {
hookComponent.$message({
type: 'error',
content: i18n.global.t('system.checkText.checkFormFail')
})
}
}
})
watch(
() => isShow.value,
(val) => {
if (val) {
method.getDialogType()
method.getCombobox()
data.form = props.form
data.menusSelectedList = data.form.detailList.map((item) => item.menu_id)
}
}
)
</script>
<style scoped lang="less">
</style>
/// <summary>
/// rolemenu controller
/// </summary>
[Route("rolemenu")]
[ApiController]
[ApiExplorerSettings(GroupName = "Base")]
public class RolemenuController : BaseController
{
#region Args
/// <summary>
/// rolemenu Service
/// </summary>
private readonly IRolemenuService _rolemenuService;
/// <summary>
/// Localizer Service
/// </summary>
private readonly IStringLocalizer<ModernWMS.Core.MultiLanguage> _stringLocalizer;
#endregion
#region constructor
/// <summary>
/// constructor
/// </summary>
/// <param name="rolemenuService">rolemenu Service</param>
/// <param name="stringLocalizer">Localizer</param>
public RolemenuController(
IRolemenuService rolemenuService
, IStringLocalizer<ModernWMS.Core.MultiLanguage> stringLocalizer
)
{
this._rolemenuService = rolemenuService;
this._stringLocalizer = stringLocalizer;
}
#endregion
#region Api
/// <summary>
/// 通过角色ID获取菜单
/// </summary>
/// <param name="userrole_id">user role id</param>
/// <returns></returns>
[HttpGet("authority")]
public async Task<ResultModel<List<MenuViewModel>>> GetMenusByRoleId(int userrole_id)
{
var data = await _rolemenuService.GetMenusByRoleId(userrole_id);
if (data.Any())
{
return ResultModel<List<MenuViewModel>>.Success(data);
}
else
{
return ResultModel<List<MenuViewModel>>.Success(new List<MenuViewModel>());
}
}
/// <summary>
/// get all records
/// </summary>
/// <returns>args</returns>
[HttpGet("all")]
public async Task<ResultModel<List<RolemenuListViewModel>>> GetAllAsync()
{
var data = await _rolemenuService.GetAllAsync(CurrentUser);
if (data.Any())
{
return ResultModel<List<RolemenuListViewModel>>.Success(data);
}
else
{
return ResultModel<List<RolemenuListViewModel>>.Success(new List<RolemenuListViewModel>());
}
}
/// <summary>
/// Get all menus
/// </summary>
/// <returns></returns>
[HttpGet("menus")]
public async Task<ResultModel<List<MenuViewModel>>> GetAllMenusAsync()
{
var data = await _rolemenuService.GetAllMenusAsync(CurrentUser);
if (data.Any())
{
return ResultModel<List<MenuViewModel>>.Success(data);
}
else
{
return ResultModel<List<MenuViewModel>>.Success(new List<MenuViewModel>());
}
}
/// <summary>
/// get a record by id
/// </summary>
/// <returns>args</returns>
[HttpGet]
public async Task<ResultModel<RolemenuBothViewModel>> GetAsync(int userrole_id)
{
var data = await _rolemenuService.GetAsync(userrole_id);
if (data != null && data.userrole_id > 0)
{
return ResultModel<RolemenuBothViewModel>.Success(data);
}
else
{
return ResultModel<RolemenuBothViewModel>.Error(_stringLocalizer["not_exists_entity"]);
}
}
/// <summary>
/// add a new record
/// </summary>
/// <param name="viewModel">args</param>
/// <returns></returns>
[HttpPost]
public async Task<ResultModel<int>> AddAsync(RolemenuBothViewModel viewModel)
{
var (id, msg) = await _rolemenuService.AddAsync(viewModel, CurrentUser);
if (id > 0)
{
return ResultModel<int>.Success(id);
}
else
{
return ResultModel<int>.Error(msg);
}
}
/// <summary>
/// update a record
/// </summary>
/// <param name="viewModel">args</param>
/// <returns></returns>
[HttpPut]
public async Task<ResultModel<bool>> UpdateAsync(RolemenuBothViewModel viewModel)
{
var (flag, msg) = await _rolemenuService.UpdateAsync(viewModel, CurrentUser);
if (flag)
{
return ResultModel<bool>.Success(flag);
}
else
{
return ResultModel<bool>.Error(msg, 400, flag);
}
}
/// <summary>
/// delete a record
/// </summary>
/// <param name="userrole_id">userrole id</param>
/// <returns></returns>
[HttpDelete]
public async Task<ResultModel<string>> DeleteAsync(int userrole_id)
{
var (flag, msg) = await _rolemenuService.DeleteAsync(userrole_id);
if (flag)
{
return ResultModel<string>.Success(msg);
}
else
{
return ResultModel<string>.Error(msg);
}
}
#endregion
}
1、主页面代码
<template>
<div class="container">
<div>
<!-- Main Content -->
<v-card class="mt-5">
<v-card-text>
<div class="operateArea">
<v-row no-gutters>
<!-- Operate Btn -->
<v-col cols="12" sm="3" class="col">
<tooltip-btn icon="mdi-plus" :tooltip-text="$t('system.page.add')" @click="method.add()"></tooltip-btn>
<tooltip-btn icon="mdi-refresh" :tooltip-text="$t('system.page.refresh')" @click="method.refresh()"></tooltip-btn>
<tooltip-btn icon="mdi-database-import-outline" :tooltip-text="$t('system.page.import')" @click="method.openDialogImport">
</tooltip-btn>
<tooltip-btn icon="mdi-export-variant" :tooltip-text="$t('system.page.export')" @click="method.exportTable"></tooltip-btn>
<tooltip-btn icon="mdi-lock-reset" :tooltip-text="$t('base.userManagement.restPwd')" @click="method.restPwd"></tooltip-btn>
</v-col>
<!-- Search Input -->
<v-col cols="12" sm="9">
<v-row no-gutters @keyup.enter="method.sureSearch">
<v-col cols="4">
<v-text-field
v-model="data.searchForm.user_num"
clearable
hide-details
density="comfortable"
class="searchInput ml-5 mt-1"
:label="$t('base.userManagement.user_num')"
variant="solo"
>
</v-text-field>
</v-col>
<v-col cols="4">
<v-text-field
v-model="data.searchForm.user_name"
clearable
hide-details
density="comfortable"
class="searchInput ml-5 mt-1"
:label="$t('base.userManagement.user_name')"
variant="solo"
>
</v-text-field>
</v-col>
<v-col cols="4">
<v-text-field
v-model="data.searchForm.user_role"
clearable
hide-details
density="comfortable"
class="searchInput ml-5 mt-1"
:label="$t('base.userManagement.user_role')"
variant="solo"
>
</v-text-field>
</v-col>
</v-row>
</v-col>
</v-row>
</div>
<!-- Table -->
<div
class="mt-5"
:style="{
height: cardHeight
}"
>
<vxe-table ref="xTable" :data="data.tableData" :height="tableHeight" align="center">
<template #empty>
{{ i18n.global.t('system.page.noData') }}
</template>
<vxe-column type="seq" width="60"></vxe-column>
<vxe-column type="checkbox" width="50"></vxe-column>
<vxe-column field="user_num" :title="$t('base.userManagement.user_num')"></vxe-column>
<vxe-column field="user_name" :title="$t('base.userManagement.user_name')"></vxe-column>
<vxe-column field="user_role" :title="$t('base.userManagement.user_role')"></vxe-column>
<vxe-column field="sex" :title="$t('base.userManagement.sex')">
<template #default="{ row }">
<span>{{ $t(`system.combobox.sex.${row.sex}`) }}</span>
</template>
</vxe-column>
<vxe-column field="contact_tel" :title="$t('base.userManagement.contact_tel')"></vxe-column>
<vxe-column field="is_valid" :title="$t('base.userManagement.is_valid')">
<template #default="{ row, column }">
<span>{{ row[column.property] ? $t('system.combobox.yesOrNo.yes') : $t('system.combobox.yesOrNo.no') }}</span>
</template>
</vxe-column>
<vxe-column field="operate" :title="$t('system.page.operate')" width="160" :resizable="false" show-overflow>
<template #default="{ row }">
<tooltip-btn
:flat="true"
icon="mdi-pencil-outline"
:tooltip-text="$t('system.page.edit')"
@click="method.editRow(row)"
></tooltip-btn>
<tooltip-btn
:flat="true"
icon="mdi-delete-outline"
:tooltip-text="$t('system.page.delete')"
:icon-color="errorColor"
@click="method.deleteRow(row)"
></tooltip-btn>
</template>
</vxe-column>
</vxe-table>
<custom-pager
:current-page="data.tablePage.pageIndex"
:page-size="data.tablePage.pageSize"
perfect
:total="data.tablePage.total"
:page-sizes="PAGE_SIZE"
:layouts="PAGE_LAYOUT"
@page-change="method.handlePageChange"
>
</custom-pager>
<!-- <vxe-grid v-bind="data.gridOptions">
<template #pager>
<custom-pagerr
v-model:current-page="data.tablePage.pageIndex"
v-model:page-size="data.tablePage.pageSize"
:layouts="['Sizes', 'PrevJump', 'PrevPage', 'Number', 'NextPage', 'NextJump', 'FullJump', 'Total']"
:total="data.tablePage.total"
@page-change="handlePageChange"
>
</custom-pagerr>
</template>
</vxe-grid> -->
</div>
</v-card-text>
</v-card>
</div>
<!-- Add or modify data mode window -->
<addOrUpdateDialog :show-dialog="data.showDialog" :form="data.dialogForm" @close="method.closeDialog" @saveSuccess="method.saveSuccess" />
<import-table :show-dialog="data.showDialogImport" @close="method.closeDialogImport" @saveSuccess="method.saveSuccessImport" />
</div>
</template>
<script lang="ts" setup>
import { computed, reactive, onMounted, ref, watch } from 'vue'
import { VxePagerEvents } from 'vxe-table'
import { computedCardHeight, computedTableHeight, errorColor } from '@/constant/style'
import tooltipBtn from '@/components/tooltip-btn.vue'
import { DataProps, UserVO } from '@/types/Base/UserManagement'
import { getUserList, deleteUser, resetPassword } from '@/api/base/userManagement'
import { hookComponent } from '@/components/system'
import addOrUpdateDialog from './add-or-update-user.vue'
import { PAGE_SIZE, PAGE_LAYOUT, DEFAULT_PAGE_SIZE } from '@/constant/vxeTable'
import i18n from '@/languages/i18n'
import customPager from '@/components/custom-pager.vue'
import { setSearchObject } from '@/utils/common'
import importTable from './import-table.vue'
import { exportData } from '@/utils/exportTable'
import { DEBOUNCE_TIME } from '@/constant/system'
const xTable = ref()
const data: DataProps = reactive({
showDialogImport: false,
searchForm: {
user_num: '',
user_name: '',
user_role: ''
},
timer: null,
tableData: [],
tablePage: {
total: 0,
pageIndex: 1,
pageSize: DEFAULT_PAGE_SIZE
},
// Dialog info
showDialog: false,
dialogForm: {
id: 0,
user_num: '',
user_name: '',
contact_tel: '',
auth_string: '',
is_valid: true
}
})
const method = reactive({
// Import Dialog
openDialogImport: () => {
data.showDialogImport = true
},
closeDialogImport: () => {
data.showDialogImport = false
},
saveSuccessImport: () => {
method.refresh()
method.closeDialogImport()
},
sureSearch: () => {
data.tablePage.searchObjects = setSearchObject(data.searchForm)
method.getUserList()
},
// Find Data by Pagination
getUserList: async () => {
const { data: res } = await getUserList(data.tablePage)
if (!res.isSuccess) {
hookComponent.$message({
type: 'error',
content: res.errorMessage
})
return
}
data.tableData = res.data.rows
data.tablePage.total = res.data.totals
},
// Add user
add: () => {
data.dialogForm = {
id: 0,
user_num: '',
user_name: '',
contact_tel: '',
auth_string: '',
is_valid: true
}
data.showDialog = true
},
// Shut add or update dialog
closeDialog: () => {
data.showDialog = false
},
// after Add or update success.
saveSuccess: () => {
method.refresh()
method.closeDialog()
},
// Refresh data
refresh: () => {
method.getUserList()
},
editRow(row: UserVO) {
data.dialogForm = JSON.parse(JSON.stringify(row))
data.showDialog = true
},
deleteRow(row: UserVO) {
hookComponent.$dialog({
content: i18n.global.t('system.tips.beforeDeleteMessage'),
handleConfirm: async () => {
if (row.id) {
const { data: res } = await deleteUser(row.id)
if (!res.isSuccess) {
hookComponent.$message({
type: 'error',
content: res.errorMessage
})
return
}
hookComponent.$message({
type: 'success',
content: `${ i18n.global.t('system.page.delete') }${ i18n.global.t('system.tips.success') }`
})
method.refresh()
}
}
})
},
// When change paging
handlePageChange: ref<VxePagerEvents.PageChange>(({ currentPage, pageSize }) => {
data.tablePage.pageIndex = currentPage
data.tablePage.pageSize = pageSize
method.refresh()
}),
// Export table
exportTable: () => {
const $table = xTable.value
exportData({
table: $table,
filename: i18n.global.t('router.sideBar.userManagement'),
columnFilterMethod({ column }: any) {
return !['checkbox'].includes(column?.type) && !['operate'].includes(column?.field)
}
})
},
// Reset password
restPwd: async () => {
const checkRecord: UserVO[] = xTable.value.getCheckboxRecords(true)
if (checkRecord.length > 0) {
hookComponent.$dialog({
content: i18n.global.t('base.userManagement.beforeResetPwd'),
handleConfirm: async () => {
const idList = checkRecord.map((item) => item.id)
const { data: res } = await resetPassword(idList)
if (!res.isSuccess) {
hookComponent.$message({
type: 'error',
content: res.errorMessage
})
return
}
hookComponent.$dialog({
content: `${ i18n.global.t('base.userManagement.afterResetPwd') } ${ res.data }`
})
}
})
} else {
hookComponent.$message({
type: 'error',
content: i18n.global.t('base.userManagement.checkboxIsNull')
})
}
}
})
onMounted(async () => {
await method.getUserList()
})
const cardHeight = computed(() => computedCardHeight({ hasTab: false }))
const tableHeight = computed(() => computedTableHeight({ hasTab: false }))
watch(
() => data.searchForm,
() => {
// debounce
if (data.timer) {
clearTimeout(data.timer)
}
data.timer = setTimeout(() => {
data.timer = null
method.sureSearch()
}, DEBOUNCE_TIME)
},
{
deep: true
}
)
</script>
<style scoped lang="less">
.operateArea {
width: 100%;
min-width: 760px;
display: flex;
align-items: center;
border-radius: 10px;
padding: 0 10px;
}
.col {
display: flex;
align-items: center;
}
</style>
2、窗体代码
<template>
<v-dialog v-model="isShow" :width="'30%'" transition="dialog-top-transition" :persistent="true">
<template #default>
<v-card>
<v-toolbar color="white" :title="`${$t('router.sideBar.userManagement')}`"></v-toolbar>
<v-card-text>
<v-form ref="formRef">
<v-text-field
v-model="data.form.user_num"
:label="$t('base.userManagement.user_num')"
:rules="data.rules.user_num"
variant="outlined"
></v-text-field>
<v-text-field
v-model="data.form.user_name"
:label="$t('base.userManagement.user_name')"
:rules="data.rules.user_name"
variant="outlined"
></v-text-field>
<v-select
v-model="data.form.user_role"
:items="data.combobox.user_role"
:rules="data.rules.user_role"
:label="$t('base.userManagement.user_role')"
variant="outlined"
clearable
></v-select>
<v-select
v-model="data.form.sex"
:items="data.combobox.sex"
item-title="label"
item-value="value"
:rules="data.rules.sex"
:label="$t('base.userManagement.sex')"
variant="outlined"
clearable
></v-select>
<v-text-field
v-model="data.form.contact_tel"
:label="$t('base.userManagement.contact_tel')"
:rules="data.rules.contact_tel"
variant="outlined"
></v-text-field>
<v-switch
v-model="data.form.is_valid"
color="primary"
:label="$t('base.userManagement.is_valid')"
:rules="data.rules.is_valid"
></v-switch>
</v-form>
</v-card-text>
<v-card-actions class="justify-end">
<v-btn variant="text" @click="method.closeDialog">{{ $t('system.page.close') }}</v-btn>
<v-btn color="primary" variant="text" @click="method.submit">{{ $t('system.page.submit') }}</v-btn>
</v-card-actions>
</v-card>
</template>
</v-dialog>
</template>
<script lang="ts" setup>
import { reactive, computed, ref, watch } from 'vue'
import { UserVO } from '@/types/Base/UserManagement'
import i18n from '@/languages/i18n'
import { hookComponent } from '@/components/system/index'
import { addUser, updateUser, getSelectItem } from '@/api/base/userManagement'
import { StringLength } from '@/utils/dataVerification/formRule'
const formRef = ref()
const emit = defineEmits(['close', 'saveSuccess'])
const props = defineProps<{
showDialog: boolean
form: UserVO
}>()
const isShow = computed(() => props.showDialog)
const dialogTitle = computed(() => {
if (props.form.id && props.form.id > 0) {
return 'update'
}
return 'add'
})
const data = reactive({
form: ref<UserVO>({
id: 0,
user_num: '',
user_name: '',
contact_tel: '',
is_valid: true
}),
rules: {
user_num: [
(val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.userManagement.user_num') }!`,
(val: string) => StringLength(val, 0, 128) === '' || StringLength(val, 0, 128)
],
user_name: [
(val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.userManagement.user_name') }!`,
(val: string) => StringLength(val, 0, 128) === '' || StringLength(val, 0, 128)
],
user_role: [(val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.userManagement.user_role') }!`],
sex: [(val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.userManagement.sex') }!`],
contact_tel: [(val: string) => StringLength(val, 0, 128) === '' || StringLength(val, 0, 64)],
is_valid: []
},
combobox: ref<{
sex: {
label: string
value: string
}[]
user_role: string[]
}>({
sex: [],
user_role: []
})
})
const method = reactive({
// Get the options required by the drop-down box
getCombobox: async () => {
// Static drop-down box
const sexOptions = ['male', 'female']
data.combobox.sex = []
for (const sex of sexOptions) {
data.combobox.sex.push({
label: i18n.global.t(`system.combobox.sex.${ sex }`),
value: sex
})
}
// Dynamic drop-down box
data.combobox.user_role = []
const { data: res } = await getSelectItem()
if (!res.isSuccess) {
return
}
for (const item of res.data) {
switch (item.code) {
case 'user_role':
data.combobox.user_role.push(item.name)
break
}
}
},
closeDialog: () => {
emit('close')
},
submit: async () => {
const { valid } = await formRef.value.validate()
if (valid) {
const { data: res } = dialogTitle.value === 'add' ? await addUser(data.form) : await updateUser(data.form)
if (!res.isSuccess) {
hookComponent.$message({
type: 'error',
content: res.errorMessage
})
return
}
hookComponent.$message({
type: 'success',
content: `${ i18n.global.t('system.page.submit') }${ i18n.global.t('system.tips.success') }`
})
emit('saveSuccess')
} else {
hookComponent.$message({
type: 'error',
content: i18n.global.t('system.checkText.checkFormFail')
})
}
}
})
watch(
() => isShow.value,
(val) => {
if (val) {
method.getCombobox()
data.form = props.form
}
}
)
</script>
<style scoped lang="less">
.v-form {
div {
margin-bottom: 7px;
}
}
</style>
<!-- UserManagement Setting Import Dialog -->
<template>
<v-dialog v-model="isShow" width="70%" transition="dialog-top-transition" :persistent="true">
<template #default>
<v-card>
<v-toolbar color="white" :title="`${$t('system.page.import')}`"></v-toolbar>
<v-card-text>
<div class="mb-4">
<tooltip-btn icon="mdi-plus" :tooltip-text="$t('system.page.chooseFile')" size="x-small" @click="method.chooseFile"></tooltip-btn>
<tooltip-btn
icon="mdi-export-variant"
:tooltip-text="$t('system.page.exportTemplate')"
size="x-small"
@click="method.exportTemplate"
></tooltip-btn>
<input v-show="false" id="open" ref="uploadExcel" type="file" accept=".xls, .xlsx" @change="method.readExcel" />
</div>
<vxe-table
ref="xTable"
:column-config="{ minWidth: '100px' }"
:data="data.importData"
:height="SYSTEM_HEIGHT.SELECT_TABLE"
:edit-config="{ trigger: 'click', mode: 'cell' }"
:edit-rules="data.validRules"
:export-config="{}"
align="center"
>
<template #empty>
{{ i18n.global.t('system.page.noData') }}
</template>
<vxe-column type="seq" width="60"></vxe-column>
<vxe-column field="operate" width="60" :title="$t('system.page.operate')" :resizable="false">
<template #default="{ row }">
<tooltip-btn
:flat="true"
icon="mdi-delete-outline"
:tooltip-text="$t('system.page.delete')"
:icon-color="errorColor"
@click="method.deleteRow(row)"
></tooltip-btn>
</template>
</vxe-column>
<vxe-column field="user_num" :title="$t('base.userManagement.user_num')"></vxe-column>
<vxe-column field="user_name" :title="$t('base.userManagement.user_name')"></vxe-column>
<vxe-column field="contact_tel" :title="$t('base.userManagement.contact_tel')"></vxe-column>
<vxe-column field="user_role" :title="$t('base.userManagement.user_role')"></vxe-column>
<vxe-column field="sex" :title="$t('base.userManagement.sex')"></vxe-column>
</vxe-table>
</v-card-text>
<v-card-actions class="justify-end">
<v-btn variant="text" @click="method.closeDialog">{{ $t('system.page.close') }}</v-btn>
<v-btn color="primary" variant="text" @click="method.submit">{{ $t('system.page.submit') }}</v-btn>
</v-card-actions>
</v-card>
</template>
</v-dialog>
</template>
<script lang="ts" setup>
import { reactive, computed, ref, watch } from 'vue'
import { VxeTablePropTypes } from 'vxe-table'
import * as XLSX from 'xlsx'
import i18n from '@/languages/i18n'
import { excelImport } from '@/api/base/userManagement'
import { hookComponent } from '@/components/system/index'
import { SYSTEM_HEIGHT, errorColor } from '@/constant/style'
import tooltipBtn from '@/components/tooltip-btn.vue'
import { ImportVO } from '@/types/Base/UserManagement'
import { exportData } from '@/utils/exportTable'
import { formatString } from '@/utils/format/formatSystem'
const emit = defineEmits(['close', 'saveSuccess'])
const uploadExcel = ref()
const xTable = ref()
const props = defineProps<{
showDialog: boolean
}>()
const isShow = computed(() => props.showDialog)
const data = reactive({
importData: ref<Array<ImportVO>>([]),
validRules: ref<VxeTablePropTypes.EditRules>({})
})
const method = reactive({
initForm: () => {
data.importData = []
},
closeDialog: () => {
emit('close')
},
submit: async () => {
const isValid = method.valid()
if (!isValid) {
return
}
const $table = xTable.value
// It must be use 'getTableData()' to get all datas with table because it will delete row sometimes.
const importData = JSON.parse(JSON.stringify($table.getTableData().fullData))
for (const item of importData) {
let sex = 'male'
if (['男', '女', 'Male', 'Female'].includes(item.sex)) {
switch (item.sex) {
case '男':
case 'Male':
sex = 'male'
break
case '女':
case 'Female':
sex = 'female'
break
}
}
item.sex = sex
}
const { data: res } = await excelImport(importData)
if (!res.isSuccess) {
hookComponent.$message({
type: 'error',
content: res.errorMessage
})
return
}
hookComponent.$message({
type: 'success',
content: `${ i18n.global.t('system.page.submit') }${ i18n.global.t('system.tips.success') }`
})
emit('saveSuccess')
},
valid: () => {
const $table = xTable.value
const importData = $table.getTableData().fullData
if (importData.length <= 0) {
hookComponent.$message({
type: 'error',
content: `${ i18n.global.t('system.tips.detailLengthIsZero') }`
})
return false
}
return true
},
chooseFile: async () => {
uploadExcel.value.value = ''
uploadExcel.value.click()
},
readExcel: async (evnt: any) => {
const files = evnt.target.files
if (files.length <= 0) {
return false
}
const file = files[0]
const fileReader = new FileReader()
fileReader.onload = (ev: ProgressEvent<FileReader>) => {
const fileData = ev?.target?.result
if (fileData) {
const workbook = XLSX.read(fileData, { type: 'binary' })
const wsname = workbook.SheetNames[0]
let ws = XLSX.utils.sheet_to_json(workbook.Sheets[wsname])
let str = JSON.stringify(ws)
str = str.replace(/(/g, '(')
str = str.replace(/)/g, ')')
ws = JSON.parse(str)
data.importData = []
ws.forEach((value: any, index: number, ws: any) => {
data.importData.push({
user_num: formatString(ws[index][i18n.global.t('base.userManagement.user_num')]),
user_name: formatString(ws[index][i18n.global.t('base.userManagement.user_name')]),
contact_tel: formatString(ws[index][i18n.global.t('base.userManagement.contact_tel')]),
user_role: formatString(ws[index][i18n.global.t('base.userManagement.user_role')]),
sex: formatString(ws[index][i18n.global.t('base.userManagement.sex')]),
is_valid: true
})
})
}
}
fileReader.readAsBinaryString(file)
},
exportTemplate: () => {
const $table = xTable.value
exportData({
table: $table,
filename: i18n.global.t('router.sideBar.userManagement'),
mode: 'header',
columnFilterMethod({ column }: any) {
return !['checkbox', 'seq'].includes(column?.type) && !['operate'].includes(column?.field)
}
})
},
deleteRow: (row: ImportVO) => {
hookComponent.$dialog({
content: i18n.global.t('system.tips.beforeDeleteDetailMessage'),
handleConfirm: async () => {
if (row) {
const $table = xTable.value
$table.remove(row)
}
}
})
}
})
watch(
() => isShow.value,
(val) => {
if (val) {
method.initForm()
}
}
)
</script>
<style scoped lang="less">
.v-form {
div {
margin-bottom: 7px;
}
}
</style>
[Route("user")]
[ApiController]
[ApiExplorerSettings(GroupName = "Base")]
public class UserController : BaseController
{
#region Args
/// <summary>
/// user Service
/// </summary>
private readonly IUserService _userService;
/// <summary>
/// Localizer Service
/// </summary>
private readonly IStringLocalizer<ModernWMS.Core.MultiLanguage> _stringLocalizer;
#endregion
#region constructor
/// <summary>
/// constructor
/// </summary>
/// <param name="userService">user Service</param>
/// <param name="stringLocalizer">Localizer</param>
public UserController(
IUserService userService
, IStringLocalizer<ModernWMS.Core.MultiLanguage> stringLocalizer
)
{
this._userService = userService;
this._stringLocalizer = stringLocalizer;
}
#endregion
#region Api
/// <summary>
/// get select items
/// </summary>
/// <returns></returns>
[HttpGet("select-item")]
public async Task<ResultModel<List<FormSelectItem>>> GetSelectItemsAsnyc()
{
var datas = await _userService.GetSelectItemsAsnyc(CurrentUser);
return ResultModel<List<FormSelectItem>>.Success(datas);
}
/// <summary>
/// page search
/// </summary>
/// <param name="pageSearch">args</param>
/// <returns></returns>
[HttpPost("list")]
public async Task<ResultModel<PageData<UserViewModel>>> PageAsync(PageSearch pageSearch)
{
var (data, totals) = await _userService.PageAsync(pageSearch, CurrentUser);
return ResultModel<PageData<UserViewModel>>.Success(new PageData<UserViewModel>
{
Rows = data,
Totals = totals
});
}
/// <summary>
/// Get all records
/// </summary>
/// <returns>args</returns>
[HttpGet("all")]
public async Task<ResultModel<List<UserViewModel>>> GetAllAsync()
{
var data = await _userService.GetAllAsync(CurrentUser);
if (data.Any())
{
return ResultModel<List<UserViewModel>>.Success(data);
}
else
{
return ResultModel<List<UserViewModel>>.Success(new List<UserViewModel>());
}
}
/// <summary>
/// Get a record by id
/// </summary>
/// <returns>args</returns>
[HttpGet]
public async Task<ResultModel<UserViewModel>> GetAsync(int id)
{
var data = await _userService.GetAsync(id);
if (data != null)
{
return ResultModel<UserViewModel>.Success(data);
}
else
{
return ResultModel<UserViewModel>.Error(_stringLocalizer["not_exists_entity"]);
}
}
/// <summary>
/// add a new record
/// </summary>
/// <param name="viewModel">args</param>
/// <returns></returns>
[HttpPost]
public async Task<ResultModel<int>> AddAsync(UserViewModel viewModel)
{
viewModel.creator = CurrentUser.user_name;
var (id, msg) = await _userService.AddAsync(viewModel, CurrentUser);
if (id > 0)
{
return ResultModel<int>.Success(id);
}
else
{
return ResultModel<int>.Error(msg);
}
}
/// <summary>
/// 注册功能实现
/// </summary>
/// <param name="viewModel">args</param>
/// <returns></returns>
[AllowAnonymous]
[HttpPost("register")]
public async Task<ResultModel<string>> Register(RegisterViewModel viewModel)
{
var (flag, msg) = await _userService.Register(viewModel);
if (flag)
{
return ResultModel<string>.Success(msg);
}
else
{
return ResultModel<string>.Error(msg);
}
}
/// <summary>
/// import users by excel
/// </summary>
/// <param name="excel_datas">excel datas</param>
/// <returns></returns>
[HttpPost("excel")]
public async Task<ResultModel<string>> ExcelAsync(List<UserExcelImportViewModel> excel_datas)
{
var (flag, msg) = await _userService.ExcelAsync(excel_datas, CurrentUser);
if (flag)
{
return ResultModel<string>.Success(msg);
}
else
{
return ResultModel<string>.Error(msg);
}
}
/// <summary>
/// update a record
/// </summary>
/// <param name="viewModel">args</param>
/// <returns></returns>
[HttpPut]
public async Task<ResultModel<bool>> UpdateAsync(UserViewModel viewModel)
{
var (flag, msg) = await _userService.UpdateAsync(viewModel, CurrentUser);
if (flag)
{
return ResultModel<bool>.Success(flag);
}
else
{
return ResultModel<bool>.Error(msg, 400, flag);
}
}
/// <summary>
/// delete a record
/// </summary>
/// <param name="id">id</param>
/// <returns></returns>
[HttpDelete]
public async Task<ResultModel<string>> DeleteAsync(int id)
{
var (flag, msg) = await _userService.DeleteAsync(id);
if (flag)
{
return ResultModel<string>.Success(msg);
}
else
{
return ResultModel<string>.Error(msg);
}
}
/// <summary>
/// reset password
/// </summary>
/// <param name="viewModel">viewmodel</param>
/// <returns></returns>
[HttpPost("reset-pwd")]
public async Task<ResultModel<string>> ResetPwd(BatchOperationViewModel viewModel)
{
var (flag, msg) = await _userService.ResetPwd(viewModel);
if (flag)
{
return ResultModel<string>.Success(msg);
}
else
{
return ResultModel<string>.Error(msg);
}
}
/// <summary>
/// change password
/// </summary>
/// <param name="viewModel">viewmodel</param>
/// <returns></returns>
[HttpPost("change-pwd")]
public async Task<ResultModel<string>> ChangePwd(UserChangePwdViewModel viewModel)
{
var (flag, msg) = await _userService.ChangePwd(viewModel);
if (flag)
{
return ResultModel<string>.Success(msg);
}
else
{
return ResultModel<string>.Error(msg);
}
}
#endregion
}
1、主页面代码
<template>
<div class="container">
<div>
<!-- Main Content -->
<v-card class="mt-5">
<v-card-text>
<div class="operateArea">
<v-row no-gutters>
<!-- Operate Btn -->
<v-col cols="12" sm="3" class="col">
<tooltip-btn icon="mdi-plus" :tooltip-text="$t('system.page.add')" @click="method.add()"></tooltip-btn>
<tooltip-btn icon="mdi-refresh" :tooltip-text="$t('system.page.refresh')" @click="method.refresh()"></tooltip-btn>
<tooltip-btn icon="mdi-export-variant" :tooltip-text="$t('system.page.export')" @click="method.exportTable"></tooltip-btn>
</v-col>
<!-- Search Input -->
<v-col cols="12" sm="9">
<!-- <v-row no-gutters @keyup.enter="method.sureSearch">
<v-col cols="12" sm="4">
<v-text-field
v-model="data.searchForm.userName"
clearable
hide-details
density="comfortable"
class="searchInput ml-5 mt-1"
:label="$t('login.userName')"
variant="solo"
>
</v-text-field>
</v-col>
<v-col cols="12" sm="4">
<v-text-field
v-model="data.searchForm.userName1"
clearable
hide-details
density="comfortable"
class="searchInput ml-5 mt-1"
:label="$t('login.userName')"
variant="solo"
>
</v-text-field>
</v-col>
<v-col cols="12" sm="4">
<v-text-field
v-model="data.searchForm.userName2"
clearable
hide-details
density="comfortable"
class="searchInput ml-5 mt-1"
:label="$t('login.userName')"
variant="solo"
>
</v-text-field>
</v-col>
</v-row> -->
</v-col>
</v-row>
</div>
<!-- Table -->
<div
class="mt-5"
:style="{
height: cardHeight
}"
>
<vxe-table ref="xTable" :data="data.tableData" :height="tableHeight" :tree-config="data.tableTreeConfig" align="center">
<template #empty>
{{ i18n.global.t('system.page.noData') }}
</template>
<vxe-column type="seq" width="80"></vxe-column>
<vxe-column field="category_name" align="left" :title="$t('base.commodityCategorySetting.category_name')" tree-node></vxe-column>
<vxe-column field="creator" :title="$t('base.commodityCategorySetting.creator')"></vxe-column>
<vxe-column field="create_time" :title="$t('base.commodityCategorySetting.create_time')"></vxe-column>
<vxe-column :title="$t('system.page.operate')" width="160" :resizable="false" show-overflow>
<template #default="{ row }">
<tooltip-btn
:flat="true"
icon="mdi-pencil-outline"
:tooltip-text="$t('system.page.edit')"
@click="method.editRow(row)"
></tooltip-btn>
<tooltip-btn
:flat="true"
icon="mdi-delete-outline"
:tooltip-text="$t('system.page.delete')"
:icon-color="errorColor"
@click="method.deleteRow(row)"
></tooltip-btn>
</template>
</vxe-column>
</vxe-table>
</div>
</v-card-text>
</v-card>
</div>
<!-- Add or modify data mode window -->
<addOrUpdateDialog :show-dialog="data.showDialog" :form="data.dialogForm" @close="method.closeDialog" @saveSuccess="method.saveSuccess" />
</div>
</template>
<script lang="ts" setup>
import { computed, reactive, onMounted, ref } from 'vue'
import { computedCardHeight, computedTableHeight, errorColor } from '@/constant/style'
import tooltipBtn from '@/components/tooltip-btn.vue'
import { DataProps, CategoryVO } from '@/types/Base/CommodityCategorySetting'
import { getCategoryAll, deleteCategory } from '@/api/base/commodityCategorySetting'
import { hookComponent } from '@/components/system'
import addOrUpdateDialog from './add-or-update-category.vue'
import i18n from '@/languages/i18n'
import { exportData } from '@/utils/exportTable'
const xTable = ref()
const data: DataProps = reactive({
// searchForm: {
// userName: '',
// userName1: '',
// userName2: ''
// },
tableData: [],
tableTreeConfig: {
transform: true,
rowField: 'id',
parentField: 'parent_id'
},
// Dialog info
showDialog: false,
dialogForm: {
id: 0,
category_name: ''
}
})
const method = reactive({
sureSearch: () => {
// console.log(data.searchForm)
},
// Find Data by Pagination
getCommodityCategorySetting: async () => {
const { data: res } = await getCategoryAll()
if (!res.isSuccess) {
hookComponent.$message({
type: 'error',
content: res.errorMessage
})
return
}
data.tableData = res.data
// nextTick(() => {
// xTable.value.setAllTreeExpand(true)
// })
},
// Add user
add: () => {
data.dialogForm = {
id: 0,
category_name: ''
}
data.showDialog = true
},
// Shut add or update dialog
closeDialog: () => {
data.showDialog = false
},
// after Add or update success.
saveSuccess: () => {
method.refresh()
method.closeDialog()
},
// Refresh data
refresh: () => {
method.getCommodityCategorySetting()
},
editRow(row: CategoryVO) {
data.dialogForm = JSON.parse(JSON.stringify(row))
// If there is no parent, the parent will not be brought in_ id
if (data.dialogForm.parent_id === 0) {
delete data.dialogForm.parent_id
}
data.showDialog = true
},
deleteRow(row: CategoryVO) {
hookComponent.$dialog({
content: i18n.global.t('system.tips.beforeDeleteMessage'),
handleConfirm: async () => {
if (row.id) {
const { data: res } = await deleteCategory(row.id)
if (!res.isSuccess) {
hookComponent.$message({
type: 'error',
content: res.errorMessage
})
return
}
hookComponent.$message({
type: 'success',
content: `${ i18n.global.t('system.page.delete') }${ i18n.global.t('system.tips.success') }`
})
method.refresh()
}
}
})
},
// Export table
exportTable: () => {
const $table = xTable.value
exportData({
table: $table,
filename: i18n.global.t('router.sideBar.commodityCategorySetting'),
columnFilterMethod({ column }: any) {
return !['checkbox'].includes(column?.type)
}
})
}
})
onMounted(async () => {
await method.getCommodityCategorySetting()
})
const cardHeight = computed(() => computedCardHeight({ hasTab: false }))
const tableHeight = computed(() => computedTableHeight({ hasTab: false, hasPager: false }))
</script>
<style scoped lang="less">
.operateArea {
width: 100%;
min-width: 760px;
display: flex;
align-items: center;
border-radius: 10px;
padding: 0 10px;
}
.col {
display: flex;
align-items: center;
}
</style>
2、窗体代码
<template>
<v-dialog v-model="isShow" :width="'30%'" transition="dialog-top-transition" :persistent="true">
<template #default>
<v-card>
<v-toolbar color="white" :title="`${$t('router.sideBar.commodityCategorySetting')}`"></v-toolbar>
<v-card-text>
<v-form ref="formRef">
<v-text-field
v-model="data.form.category_name"
:label="$t('base.commodityCategorySetting.category_name')"
:rules="data.rules.category_name"
variant="outlined"
></v-text-field>
<v-select
v-model="data.form.parent_id"
:items="data.combobox.parent"
item-title="label"
item-value="value"
:rules="data.rules.parent_id"
:label="$t('base.commodityCategorySetting.parent_name')"
variant="outlined"
clearable
></v-select>
</v-form>
</v-card-text>
<v-card-actions class="justify-end">
<v-btn variant="text" @click="method.closeDialog">{{ $t('system.page.close') }}</v-btn>
<v-btn color="primary" variant="text" @click="method.submit">{{ $t('system.page.submit') }}</v-btn>
</v-card-actions>
</v-card>
</template>
</v-dialog>
</template>
<script lang="ts" setup>
import { reactive, computed, ref, watch } from 'vue'
import { CategoryVO } from '@/types/Base/CommodityCategorySetting'
import i18n from '@/languages/i18n'
import { hookComponent } from '@/components/system/index'
import { addCategory, updateCategory, getCategoryAll } from '@/api/base/commodityCategorySetting'
import { StringLength } from '@/utils/dataVerification/formRule'
const formRef = ref()
const emit = defineEmits(['close', 'saveSuccess'])
const props = defineProps<{
showDialog: boolean
form: CategoryVO
}>()
const isShow = computed(() => props.showDialog)
const dialogTitle = computed(() => {
if (props.form.id && props.form.id > 0) {
return 'update'
}
return 'add'
})
const data = reactive({
form: ref<CategoryVO>({
id: 0,
category_name: ''
}),
rules: {
category_name: [
(val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.commodityCategorySetting.category_name') }!`,
(val: string) => StringLength(val, 0, 32) === '' || StringLength(val, 0, 32)
],
parent_id: [],
is_valid: []
},
combobox: ref<{
parent: {
label: string
value: number
}[]
}>({
parent: []
})
})
const method = reactive({
// Get the options required by the drop-down box
getCombobox: async () => {
data.combobox.parent = []
const { data: res } = await getCategoryAll()
if (!res.isSuccess) {
return
}
for (const item of res.data) {
data.combobox.parent.push({
label: item.category_name,
value: item.id
})
}
},
closeDialog: () => {
emit('close')
},
submit: async () => {
const { valid } = await formRef.value.validate()
if (valid) {
const { data: res } = dialogTitle.value === 'add' ? await addCategory(data.form) : await updateCategory(data.form)
if (!res.isSuccess) {
hookComponent.$message({
type: 'error',
content: res.errorMessage
})
return
}
hookComponent.$message({
type: 'success',
content: `${ i18n.global.t('system.page.submit') }${ i18n.global.t('system.tips.success') }`
})
emit('saveSuccess')
} else {
hookComponent.$message({
type: 'error',
content: i18n.global.t('system.checkText.checkFormFail')
})
}
}
})
watch(
() => isShow.value,
(val) => {
if (val) {
method.getCombobox()
data.form = props.form
}
}
)
</script>
<style scoped lang="less">
.v-form {
div {
margin-bottom: 7px;
}
}
</style>
[Route("category")]
[ApiController]
[ApiExplorerSettings(GroupName = "Base")]
public class CategoryController : BaseController
{
#region Args
/// <summary>
/// category Service
/// </summary>
private readonly ICategoryService _categoryService;
/// <summary>
/// Localizer Service
/// </summary>
private readonly IStringLocalizer<ModernWMS.Core.MultiLanguage> _stringLocalizer;
#endregion
#region constructor
/// <summary>
/// constructor
/// </summary>
/// <param name="categoryService">category Service</param>
/// <param name="stringLocalizer">Localizer</param>
public CategoryController(
ICategoryService categoryService
, IStringLocalizer<ModernWMS.Core.MultiLanguage> stringLocalizer
)
{
this._categoryService = categoryService;
this._stringLocalizer = stringLocalizer;
}
#endregion
#region Api
/// <summary>
/// get all records
/// </summary>
/// <returns>args</returns>
[HttpGet("all")]
public async Task<ResultModel<List<CategoryViewModel>>> GetAllAsync()
{
var data = await _categoryService.GetAllAsync(CurrentUser);
if (data.Any())
{
return ResultModel<List<CategoryViewModel>>.Success(data);
}
else
{
return ResultModel<List<CategoryViewModel>>.Success(new List<CategoryViewModel>());
}
}
/// <summary>
/// get a record by id
/// </summary>
/// <returns>args</returns>
[HttpGet]
public async Task<ResultModel<CategoryViewModel>> GetAsync(int id)
{
var data = await _categoryService.GetAsync(id);
if (data != null)
{
return ResultModel<CategoryViewModel>.Success(data);
}
else
{
return ResultModel<CategoryViewModel>.Error(_stringLocalizer["not_exists_entity"]);
}
}
/// <summary>
/// add a new record
/// </summary>
/// <param name="viewModel">args</param>
/// <returns></returns>
[HttpPost]
public async Task<ResultModel<int>> AddAsync(CategoryViewModel viewModel)
{
var (id, msg) = await _categoryService.AddAsync(viewModel, CurrentUser);
if (id > 0)
{
return ResultModel<int>.Success(id);
}
else
{
return ResultModel<int>.Error(msg);
}
}
/// <summary>
/// update a record
/// </summary>
/// <param name="viewModel">args</param>
/// <returns></returns>
[HttpPut]
public async Task<ResultModel<bool>> UpdateAsync(CategoryViewModel viewModel)
{
var (flag, msg) = await _categoryService.UpdateAsync(viewModel);
if (flag)
{
return ResultModel<bool>.Success(flag);
}
else
{
return ResultModel<bool>.Error(msg, 400, flag);
}
}
/// <summary>
/// delete a record
/// </summary>
/// <param name="id">id</param>
/// <returns></returns>
[HttpDelete]
public async Task<ResultModel<string>> DeleteAsync(int id)
{
var (flag, msg) = await _categoryService.DeleteAsync(id);
if (flag)
{
return ResultModel<string>.Success(msg);
}
else
{
return ResultModel<string>.Error(msg);
}
}
#endregion
}