文章目录
- 前言
- 一、基础设置
- 1.货主信息
- 1.1 页面代码
- 1.2 接口代码
- 2.运费设置
- 2.1 页面代码
- 2.2 接口代码
- 3.客户信息
- 3.1 页面代码
- 3.2 接口代码
前言
基础设置主要分为以下几个模块:
- 首页
- 公司信息
- 角色设置
- 菜单设置
- 用户管理
- 商品类别设置
- 商品管理
- 供应商信息
- 仓库设置
- 货主信息
- 运费设置
- 客户信息
这边需要提下客户和货主的概念:客户是与仓储签订直接合同的甲方,货主是拥有货权的人。
实际情况下,货主会委托某人或某企业管理自己的货物,而这个管理人或企业会与仓储签订合同,使用仓储的软硬件资源。
货主是客户的甲方,客户是仓储的甲方(货主和客户也可能是同一人或企业)。
一、基础设置
1.货主信息
1.1 页面代码
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></v-col><!-- Search Input --><v-col cols="12" sm="9"><v-row no-gutters @keyup.enter="method.sureSearch"><v-col cols="4"> </v-col><v-col cols="4"> </v-col><v-col cols="4"><v-text-fieldv-model="data.searchForm.goods_owner_name"clearablehide-detailsdensity="comfortable"class="searchInput ml-5 mt-1":label="$t('base.ownerOfCargo.goods_owner_name')"variant="solo"></v-text-field></v-col></v-row></v-col></v-row></div><!-- Table --><divclass="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 field="goods_owner_name" :title="$t('base.ownerOfCargo.goods_owner_name')"></vxe-column><vxe-column field="city" :title="$t('base.ownerOfCargo.city')"></vxe-column><vxe-column field="address" :title="$t('base.ownerOfCargo.address')"></vxe-column><vxe-column field="contact_tel" :title="$t('base.ownerOfCargo.contact_tel')"></vxe-column><vxe-column field="manager" :title="$t('base.ownerOfCargo.manager')"></vxe-column><vxe-column field="creator" :title="$t('base.ownerOfCargo.creator')"></vxe-column><vxe-column field="create_time" :title="$t('base.ownerOfCargo.create_time')"></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></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 { OwnerOfCargoVO, DataProps } from '@/types/Base/OwnerOfCargo'
import { getOwnerOfCargoByPage, deleteOwnerOfCargo } from '@/api/base/ownerOfCargo'
import { hookComponent } from '@/components/system'
import addOrUpdateDialog from './add-or-update-owner-of-cargo.vue'
import i18n from '@/languages/i18n'
import importTable from './import-table.vue'
import { setSearchObject } from '@/utils/common'
import customPager from '@/components/custom-pager.vue'
import { PAGE_SIZE, PAGE_LAYOUT, DEFAULT_PAGE_SIZE } from '@/constant/vxeTable'
import { exportData } from '@/utils/exportTable'
import { DEBOUNCE_TIME } from '@/constant/system'const xTable = ref()const data: DataProps = reactive({searchForm: {goods_owner_name: ''},showDialogImport: false,tableData: [],// Dialog infoshowDialog: false,dialogForm: {id: 0,goods_owner_name: '',city: '',address: '',contact_tel: '',manager: ''},tablePage: {total: 0,pageIndex: 1,pageSize: DEFAULT_PAGE_SIZE},timer: null
})const method = reactive({// When change paginghandlePageChange: ref<VxePagerEvents.PageChange>(({ currentPage, pageSize }) => {data.tablePage.pageIndex = currentPagedata.tablePage.pageSize = pageSizemethod.refresh()}),// Import DialogopenDialogImport: () => {data.showDialogImport = true},closeDialogImport: () => {data.showDialogImport = false},saveSuccessImport: () => {method.refresh()method.closeDialogImport()},sureSearch: () => {data.tablePage.searchObjects = setSearchObject(data.searchForm)method.getOwnerOfCargoList()},// Find Data by PaginationgetOwnerOfCargoList: async () => {const { data: res } = await getOwnerOfCargoByPage(data.tablePage)if (!res.isSuccess) {hookComponent.$message({type: 'error',content: res.errorMessage})return}data.tableData = res.data.rowsdata.tablePage.total = res.data.totals},// Add useradd: () => {data.dialogForm = {id: 0,goods_owner_name: '',city: '',address: '',contact_tel: '',manager: ''}data.showDialog = true},// Shut add or update dialogcloseDialog: () => {data.showDialog = false},// after Add or update success.saveSuccess: () => {method.refresh()method.closeDialog()},// Refresh datarefresh: () => {method.getOwnerOfCargoList()},editRow(row: OwnerOfCargoVO) {data.dialogForm = JSON.parse(JSON.stringify(row))data.showDialog = true},deleteRow(row: OwnerOfCargoVO) {hookComponent.$dialog({content: i18n.global.t('system.tips.beforeDeleteMessage'),handleConfirm: async () => {if (row.id) {const { data: res } = await deleteOwnerOfCargo(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 tableexportTable: () => {const $table = xTable.valueexportData({table: $table,filename: i18n.global.t('router.sideBar.ownerOfCargo'),columnFilterMethod({ column }: any) {return !['checkbox'].includes(column?.type) && !['operate'].includes(column?.field)}})}
})onMounted(async () => {await method.getOwnerOfCargoList()
})const cardHeight = computed(() => computedCardHeight({ hasTab: false }))const tableHeight = computed(() => computedTableHeight({ hasTab: false }))watch(() => data.searchForm,() => {// debounceif (data.timer) {clearTimeout(data.timer)}data.timer = setTimeout(() => {data.timer = nullmethod.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.ownerOfCargo')}`"></v-toolbar><v-card-text><v-form ref="formRef"><v-text-fieldv-model="data.form.goods_owner_name":label="$t('base.ownerOfCargo.goods_owner_name')":rules="data.rules.goods_owner_name"variant="outlined"></v-text-field><v-text-field v-model="data.form.city" :label="$t('base.ownerOfCargo.city')" :rules="data.rules.city" variant="outlined"></v-text-field><v-text-fieldv-model="data.form.address":label="$t('base.ownerOfCargo.address')":rules="data.rules.address"variant="outlined"></v-text-field><v-text-fieldv-model="data.form.manager":label="$t('base.ownerOfCargo.manager')":rules="data.rules.manager"variant="outlined"></v-text-field><v-text-fieldv-model="data.form.contact_tel":label="$t('base.ownerOfCargo.contact_tel')":rules="data.rules.contact_tel"variant="outlined"></v-text-field></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 { OwnerOfCargoVO } from '@/types/Base/OwnerOfCargo'
import i18n from '@/languages/i18n'
import { hookComponent } from '@/components/system/index'
import { addOwnerOfCargo, updateOwnerOfCargo } from '@/api/base/ownerOfCargo'
import { StringLength } from '@/utils/dataVerification/formRule'const formRef = ref()
const emit = defineEmits(['close', 'saveSuccess'])const props = defineProps<{showDialog: booleanform: OwnerOfCargoVO
}>()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<OwnerOfCargoVO>({id: 0,goods_owner_name: '',city: '',address: '',manager: '',contact_tel: ''}),rules: {goods_owner_name: [(val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.ownerOfCargo.goods_owner_name') }!`,(val: string) => StringLength(val, 0, 256) === '' || StringLength(val, 0, 256)],city: [(val: string) => StringLength(val, 0, 128) === '' || StringLength(val, 0, 128)],address: [(val: string) => StringLength(val, 0, 256) === '' || StringLength(val, 0, 256)],manager: [(val: string) => StringLength(val, 0, 64) === '' || StringLength(val, 0, 64)],contact_tel: [(val: string) => StringLength(val, 0, 64) === '' || StringLength(val, 0, 64)]}
})const method = reactive({closeDialog: () => {emit('close')},submit: async () => {const { valid } = await formRef.value.validate()if (valid) {const { data: res } = dialogTitle.value === 'add' ? await addOwnerOfCargo(data.form) : await updateOwnerOfCargo(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) {data.form = props.form}}
)
</script><style scoped lang="less">
.v-form {div {margin-bottom: 7px;}
}
</style>
1.2 接口代码
[Route("goodsowner")]
[ApiController]
[ApiExplorerSettings(GroupName = "Base")]
public class GoodsownerController : BaseController
{#region Args/// <summary>/// goodsowner Service/// </summary>private readonly IGoodsownerService _goodsownerService;/// <summary>/// Localizer Service/// </summary>private readonly IStringLocalizer<ModernWMS.Core.MultiLanguage> _stringLocalizer;#endregion#region constructor/// <summary>/// constructor/// </summary>/// <param name="goodsownerService">goodsowner Service</param>/// <param name="stringLocalizer">Localizer</param>public GoodsownerController(IGoodsownerService goodsownerService, IStringLocalizer<ModernWMS.Core.MultiLanguage> stringLocalizer){this._goodsownerService = goodsownerService;this._stringLocalizer = stringLocalizer;}#endregion#region Api/// <summary>/// page search/// </summary>/// <param name="pageSearch">args</param>/// <returns></returns>[HttpPost("list")]public async Task<ResultModel<PageData<GoodsownerViewModel>>> PageAsync(PageSearch pageSearch){var (data, totals) = await _goodsownerService.PageAsync(pageSearch, CurrentUser);return ResultModel<PageData<GoodsownerViewModel>>.Success(new PageData<GoodsownerViewModel>{Rows = data,Totals = totals});}/// <summary>/// Get all records/// </summary>/// <returns>args</returns>[HttpGet("all")]public async Task<ResultModel<List<GoodsownerViewModel>>> GetAllAsync(){var data = await _goodsownerService.GetAllAsync(CurrentUser);if (data.Any()){return ResultModel<List<GoodsownerViewModel>>.Success(data);}else{return ResultModel<List<GoodsownerViewModel>>.Success(new List<GoodsownerViewModel>());}}/// <summary>/// get a record by id/// </summary>/// <returns>args</returns>[HttpGet]public async Task<ResultModel<GoodsownerViewModel>> GetAsync(int id){var data = await _goodsownerService.GetAsync(id);if (data != null && data.id > 0){return ResultModel<GoodsownerViewModel>.Success(data);}else{return ResultModel<GoodsownerViewModel>.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(GoodsownerViewModel viewModel){var (id, msg) = await _goodsownerService.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(GoodsownerViewModel viewModel){var (flag, msg) = await _goodsownerService.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 _goodsownerService.DeleteAsync(id);if (flag){return ResultModel<string>.Success(msg);}else{return ResultModel<string>.Error(msg);}}#endregion#region Import/// <summary>/// import goodsowners by excel/// </summary>/// <param name="input">excel data</param>/// <returns></returns>[HttpPost("excel")]public async Task<ResultModel<List<GoodsownerImportViewModel>>> ExcelAsync(List<GoodsownerImportViewModel> input){var (flag, errorData) = await _goodsownerService.ExcelAsync(input, CurrentUser);if (flag){return ResultModel<List<GoodsownerImportViewModel>>.Success(errorData);}else{return ResultModel<List<GoodsownerImportViewModel>>.Error("", 400, errorData);}}#endregion
}
2.运费设置
2.1 页面代码
1、主页面代码
<!-- Freight Setting -->
<template><div class="container"><div><!-- Main Content --><v-card class="mt-5"><v-card-text><v-window v-model="data.activeTab"><v-window-item><div class="operateArea"><v-row no-gutters><!-- Operate Btn --><v-col cols="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></v-col><!-- Search Input --><v-col cols="9"><v-row no-gutters @keyup.enter="method.sureSearch"><v-col cols="4"><v-text-fieldv-model="data.searchForm.carrier"clearablehide-detailsdensity="comfortable"class="searchInput ml-5 mt-1":label="$t('base.freightSetting.carrier')"variant="solo"></v-text-field></v-col><v-col cols="4"><v-text-fieldv-model="data.searchForm.departure_city"clearablehide-detailsdensity="comfortable"class="searchInput ml-5 mt-1":label="$t('base.freightSetting.departure_city')"variant="solo"></v-text-field></v-col><v-col cols="4"><v-text-fieldv-model="data.searchForm.arrival_city"clearablehide-detailsdensity="comfortable"class="searchInput ml-5 mt-1":label="$t('base.freightSetting.arrival_city')"variant="solo"></v-text-field></v-col></v-row></v-col></v-row></div><!-- Table --><divclass="mt-5":style="{height: cardHeight}"><vxe-table ref="xTable" :column-config="{ minWidth: '100px' }" :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="carrier" :title="$t('base.freightSetting.carrier')"></vxe-column><vxe-column field="departure_city" :title="$t('base.freightSetting.departure_city')"></vxe-column><vxe-column field="arrival_city" :title="$t('base.freightSetting.arrival_city')"></vxe-column><vxe-column field="price_per_weight" :title="$t('base.freightSetting.price_per_weight')"></vxe-column><vxe-column field="price_per_volume" :title="$t('base.freightSetting.price_per_volume')"></vxe-column><vxe-column field="min_payment" :title="$t('base.freightSetting.min_payment')"></vxe-column><vxe-column field="creator" :title="$t('base.freightSetting.creator')"></vxe-column><vxe-column field="create_time" width="170px" :title="$t('base.freightSetting.create_time')"></vxe-column><vxe-column field="is_valid" :title="$t('base.freightSetting.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></div></v-window-item></v-window></v-card-text></v-card><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></div>
</template><script lang="ts" setup>
import { computed, ref, reactive, onMounted, watch } from 'vue'
import { VxePagerEvents } from 'vxe-table'
import { computedCardHeight, computedTableHeight, errorColor } from '@/constant/style'
import { FreightVO } from '@/types/Base/Freight'
import { PAGE_SIZE, PAGE_LAYOUT, DEFAULT_PAGE_SIZE } from '@/constant/vxeTable'
import { hookComponent } from '@/components/system'
import { deleteFreight, getFreightList } from '@/api/base/freightSetting'
import { DEBOUNCE_TIME } from '@/constant/system'
import { setSearchObject } from '@/utils/common'
import { SearchObject } from '@/types/System/Form'
import tooltipBtn from '@/components/tooltip-btn.vue'
import customPager from '@/components/custom-pager.vue'
import addOrUpdateDialog from './add-or-update-freight.vue'
import importTable from './import-table.vue'
import i18n from '@/languages/i18n'
import { exportData } from '@/utils/exportTable'const xTable = ref()const data = reactive({showDialog: false,showDialogImport: false,dialogForm: {id: 0,carrier: '',departure_city: '',arrival_city: '',price_per_weight: 0,price_per_volume: 0,min_payment: 0,is_valid: true},searchForm: {carrier: '',departure_city: '',arrival_city: ''},activeTab: null,tableData: ref<FreightVO[]>([]),tablePage: reactive({total: 0,pageIndex: 1,pageSize: DEFAULT_PAGE_SIZE,searchObjects: ref<Array<SearchObject>>([])}),timer: ref<any>(null)
})const method = reactive({// Open a dialog to addadd: () => {data.dialogForm = {id: 0,carrier: '',departure_city: '',arrival_city: '',price_per_weight: 0,price_per_volume: 0,min_payment: 0,is_valid: true}data.showDialog = true},// Shut add or update dialogcloseDialog: () => {data.showDialog = false},// After add or update success.saveSuccess: () => {method.refresh()method.closeDialog()},// Import DialogopenDialogImport: () => {data.showDialogImport = true},closeDialogImport: () => {data.showDialogImport = false},saveSuccessImport: () => {method.refresh()method.closeDialogImport()},// Refresh datarefresh: () => {method.getFreightList()},getFreightList: async () => {const { data: res } = await getFreightList(data.tablePage)if (!res.isSuccess) {hookComponent.$message({type: 'error',content: res.errorMessage})return}data.tableData = res.data.rowsdata.tablePage.total = res.data.totals},editRow(row: FreightVO) {data.dialogForm = JSON.parse(JSON.stringify(row))data.showDialog = true},deleteRow(row: FreightVO) {hookComponent.$dialog({content: i18n.global.t('system.tips.beforeDeleteMessage'),handleConfirm: async () => {if (row.id) {const { data: res } = await deleteFreight(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()}}})},handlePageChange: ref<VxePagerEvents.PageChange>(({ currentPage, pageSize }) => {data.tablePage.pageIndex = currentPagedata.tablePage.pageSize = pageSizemethod.getFreightList()}),exportTable: () => {const $table = xTable.valueexportData({table: $table,filename: i18n.global.t('router.sideBar.freightSetting'),columnFilterMethod({ column }: any) {return !['checkbox'].includes(column?.type) && !['operate'].includes(column?.field)}})},importTable: () => {const $table = xTable.value$table.importData()},sureSearch: () => {data.tablePage.searchObjects = setSearchObject(data.searchForm)method.getFreightList()}
})onMounted(() => {method.getFreightList()
})const cardHeight = computed(() => computedCardHeight({ hasTab: false }))
const tableHeight = computed(() => computedTableHeight({ hasTab: false }))watch(() => data.searchForm,() => {// debounceif (data.timer) {clearTimeout(data.timer)}data.timer = setTimeout(() => {data.timer = nullmethod.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、窗体代码
<!-- Freight Setting Operate Dialog -->
<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.freightSetting')}`"></v-toolbar><v-card-text><v-form ref="formRef"><v-text-fieldv-model="data.form.carrier":label="$t('base.freightSetting.carrier')":rules="data.rules.carrier"variant="outlined"></v-text-field><v-text-fieldv-model="data.form.departure_city":label="$t('base.freightSetting.departure_city')":rules="data.rules.departure_city"variant="outlined"></v-text-field><v-text-fieldv-model="data.form.arrival_city":label="$t('base.freightSetting.arrival_city')":rules="data.rules.arrival_city"variant="outlined"></v-text-field><v-text-fieldv-model="data.form.price_per_weight":label="$t('base.freightSetting.price_per_weight')":rules="data.rules.price_per_weight"variant="outlined"></v-text-field><v-text-fieldv-model="data.form.price_per_volume":label="$t('base.freightSetting.price_per_volume')":rules="data.rules.price_per_volume"variant="outlined"></v-text-field><v-text-fieldv-model="data.form.min_payment":label="$t('base.freightSetting.min_payment')":rules="data.rules.min_payment"variant="outlined"></v-text-field><v-switchv-model="data.form.is_valid"color="primary":label="$t('base.freightSetting.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 i18n from '@/languages/i18n'
import { hookComponent } from '@/components/system/index'
import { addFreight, updateFreight } from '@/api/base/freightSetting'
import { FreightVO } from '@/types/Base/Freight'
import { StringLength, IsDecimal } from '@/utils/dataVerification/formRule'const formRef = ref()
const emit = defineEmits(['close', 'saveSuccess'])const props = defineProps<{showDialog: booleanform: FreightVO
}>()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<FreightVO>({id: 0,carrier: '',departure_city: '',arrival_city: '',price_per_weight: 0,price_per_volume: 0,min_payment: 0,is_valid: true}),rules: {carrier: [(val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.freightSetting.carrier') }!`,(val: string) => StringLength(val, 0, 256) === '' || StringLength(val, 0, 256)],departure_city: [(val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.freightSetting.departure_city') }!`,(val: string) => StringLength(val, 0, 128) === '' || StringLength(val, 0, 128)],arrival_city: [(val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.freightSetting.arrival_city') }!`,(val: string) => StringLength(val, 0, 128) === '' || StringLength(val, 0, 128)],price_per_weight: [(val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.freightSetting.price_per_weight') }!`,(val: number) => IsDecimal(val, 'nonNegative', 6, 2) === '' || IsDecimal(val, 'nonNegative', 6, 2)],price_per_volume: [(val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.freightSetting.price_per_volume') }!`,(val: number) => IsDecimal(val, 'nonNegative', 6, 2) === '' || IsDecimal(val, 'nonNegative', 6, 2)],min_payment: [(val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.freightSetting.min_payment') }!`,(val: number) => IsDecimal(val, 'nonNegative', 8, 2) === '' || IsDecimal(val, 'nonNegative', 8, 2)],is_valid: []}
})const method = reactive({closeDialog: () => {emit('close')},initForm: () => {data.form = props.form},submit: async () => {const { valid } = await formRef.value.validate()if (valid) {const { data: res } = dialogTitle.value === 'add' ? await addFreight(data.form) : await updateFreight(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.initForm()}}
)
</script><style scoped lang="less">
.v-form {div {margin-bottom: 7px;}
}
</style>
2.2 接口代码
[Route("freightfee")][ApiController][ApiExplorerSettings(GroupName = "Base")]public class FreightfeeController : BaseController{#region Args/// <summary>/// freightfee Service/// </summary>private readonly IFreightfeeService _freightfeeService;/// <summary>/// Localizer Service/// </summary>private readonly IStringLocalizer<ModernWMS.Core.MultiLanguage> _stringLocalizer;#endregion#region constructor/// <summary>/// constructor/// </summary>/// <param name="freightfeeService">freightfee Service</param>/// <param name="stringLocalizer">Localizer</param>public FreightfeeController(IFreightfeeService freightfeeService, IStringLocalizer<ModernWMS.Core.MultiLanguage> stringLocalizer){this._freightfeeService = freightfeeService;this._stringLocalizer= stringLocalizer;}#endregion#region Api/// <summary>/// page search/// </summary>/// <param name="pageSearch">args</param>/// <returns></returns>[HttpPost("list")]public async Task<ResultModel<PageData<FreightfeeViewModel>>> PageAsync(PageSearch pageSearch){var (data, totals) = await _freightfeeService.PageAsync(pageSearch, CurrentUser);return ResultModel<PageData<FreightfeeViewModel>>.Success(new PageData<FreightfeeViewModel>{Rows = data,Totals = totals});}/// <summary>/// get all records/// </summary>/// <returns>args</returns>[HttpGet("all")]public async Task<ResultModel<List<FreightfeeViewModel>>> GetAllAsync(){var data = await _freightfeeService.GetAllAsync(CurrentUser);if (data.Any()){return ResultModel<List<FreightfeeViewModel>>.Success(data);}else{return ResultModel<List<FreightfeeViewModel>>.Success(new List<FreightfeeViewModel>());}}/// <summary>/// get a record by id/// </summary>/// <returns>args</returns>[HttpGet]public async Task<ResultModel<FreightfeeViewModel>> GetAsync(int id){var data = await _freightfeeService.GetAsync(id);if (data!=null){return ResultModel<FreightfeeViewModel>.Success(data);}else{return ResultModel<FreightfeeViewModel>.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(FreightfeeViewModel viewModel){var (id, msg) = await _freightfeeService.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(FreightfeeViewModel viewModel){var (flag, msg) = await _freightfeeService.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 _freightfeeService.DeleteAsync(id);if (flag){return ResultModel<string>.Success(msg);}else{return ResultModel<string>.Error(msg);}}/// <summary>/// import freight fee by excel/// </summary>/// <param name="excel_datas">excel datas</param>/// <returns></returns>[HttpPost("excel")]public async Task<ResultModel<string>> ExcelAsync(List<FreightfeeExcelmportViewModel> excel_datas){var (flag, msg) = await _freightfeeService.ExcelAsync(excel_datas, CurrentUser);if (flag){return ResultModel<string>.Success(msg);}else{return ResultModel<string>.Error(msg);}}#endregion}
3.客户信息
3.1 页面代码
1、主页面代码
<!-- customer Setting -->
<template><div class="container"><div><!-- Main Content --><v-card class="mt-5"><v-card-text><!-- <v-window v-model="data.activeTab"><v-window-item> --><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></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-col><v-col cols="12" sm="4"></v-col><v-col cols="12" sm="4"><v-text-fieldv-model="data.searchForm.customer_name"clearablehide-detailsdensity="comfortable"class="searchInput ml-5 mt-1":label="$t('base.customer.customer_name')"variant="solo"></v-text-field></v-col></v-row></v-col></v-row></div><!-- Table --><divclass="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="customer_name" :title="$t('base.customer.customer_name')"></vxe-column><vxe-column field="city" :title="$t('base.customer.city')"></vxe-column><vxe-column field="address" :title="$t('base.customer.address')"></vxe-column><vxe-column field="manager" :title="$t('base.customer.manager')"></vxe-column><vxe-column field="email" :title="$t('base.customer.email')"></vxe-column><vxe-column field="contact_tel" :title="$t('base.customer.contact_tel')"></vxe-column><vxe-column field="creator" :title="$t('base.customer.creator')"></vxe-column><vxe-column field="create_time" :title="$t('base.customer.create_time')"><!-- :formatter="['formatDate', 'yyyy-MM-dd HH:mm:ss']" --><template #default="{ row, column }"><span>{{ formatDate(row[column.property]) }}</span></template></vxe-column><vxe-column field="last_update_time" :title="$t('base.customer.last_update_time')"><template #default="{ row, column }"><span>{{ formatDate(row[column.property]) }}</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></div><!-- </v-window-item></v-window> --></v-card-text></v-card></div></div><addOrUpdateDialog :show-dialog="data.showDialog" :form="data.dialogForm" @close="method.closeDialog" @saveSuccess="method.saveSuccess" /><importCustomerTable :show-dialog="data.showDialogImport" @close="method.closeDialogImport" @saveSuccess="method.saveSuccessImport" />
</template><script lang="tsx" setup>
import { computed, ref, reactive, onMounted, watch } from 'vue'
import { VxePagerEvents } from 'vxe-table'
import { computedCardHeight, computedTableHeight, errorColor } from '@/constant/style'
import { CustomerVO, DataProps } from '@/types/Base/Customer'
import { PAGE_SIZE, PAGE_LAYOUT, DEFAULT_PAGE_SIZE } from '@/constant/vxeTable'
import tooltipBtn from '@/components/tooltip-btn.vue'
import addOrUpdateDialog from './add-or-update-customer.vue'
import { hookComponent } from '@/components/system'
import { DEBOUNCE_TIME } from '@/constant/system'
import { setSearchObject } from '@/utils/common'
import { SearchObject } from '@/types/System/Form'
import i18n from '@/languages/i18n'
import { getCustomerList, deleteCustomer } from '@/api/base/customer'
import importCustomerTable from './import-customer-table.vue'
import { formatDate } from '@/utils/format/formatSystem'
import customPager from '@/components/custom-pager.vue'
import { exportData } from '@/utils/exportTable'const xTable = ref()const data = reactive({searchForm: {customer_name: ''},tableData: [],// activeTab: null,showDialog: false,showDialogImport: false,dialogForm: {id: 0,customer_name: '',city: '',address: '',manager: '',email: '',contact_tel: '',is_valid: true},tablePage: {total: 0,pageIndex: 1,pageSize: DEFAULT_PAGE_SIZE,searchObjects: ref<Array<SearchObject>>([])},timer: ref<any>(null)
})const method = reactive({// Import DialogopenDialogImport: () => {data.showDialogImport = true},closeDialogImport: () => {data.showDialogImport = false},saveSuccessImport: () => {method.refresh()method.closeDialog()},sureSearch: () => {data.tablePage.searchObjects = setSearchObject(data.searchForm)method.getData()},// Add useradd: () => {data.dialogForm = {id: 0,customer_name: '',city: '',address: '',manager: '',email: '',contact_tel: '',is_valid: true}data.showDialog = true},// Shut add or update dialogcloseDialog: () => {data.showDialog = false},// after Add or update success.saveSuccess: () => {method.refresh()method.closeDialog()},// Refresh datarefresh: () => {method.getData()},editRow(row: CustomerVO) {data.dialogForm = JSON.parse(JSON.stringify(row))data.showDialog = true},deleteRow(row: CustomerVO) {hookComponent.$dialog({content: i18n.global.t('system.tips.beforeDeleteMessage'),handleConfirm: async () => {if (row.id) {const { data: res } = await deleteCustomer(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()}}})},handlePageChange: ref<VxePagerEvents.PageChange>(({ currentPage, pageSize }) => {data.tablePage.pageIndex = currentPagedata.tablePage.pageSize = pageSizemethod.getData()}),exportTable: () => {const $table = xTable.valueexportData({table: $table,filename: i18n.global.t('router.sideBar.customer'),columnFilterMethod({ column }: any) {return !['checkbox'].includes(column?.type) && !['operate'].includes(column?.field)}})},getData: async () => {const { data: res } = await getCustomerList(data.tablePage)if (!res.isSuccess) {hookComponent.$message({type: 'error',content: res.errorMessage})return}data.tableData = res.data.rowsdata.tablePage.total = res.data.totals}
})
onMounted(() => {method.getData()
})const cardHeight = computed(() => computedCardHeight({ hasTab: false }))
const tableHeight = computed(() => computedTableHeight({ hasTab: false }))watch(() => data.searchForm,() => {// debounceif (data.timer) {clearTimeout(data.timer)}data.timer = setTimeout(() => {data.timer = nullmethod.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.customer')}`"></v-toolbar><v-card-text><v-form ref="formRef"><v-text-fieldv-model="data.form.customer_name":label="$t('base.customer.customer_name')":rules="data.rules.customer_name"variant="outlined"></v-text-field><v-text-field v-model="data.form.city" :label="$t('base.customer.city')" :rules="data.rules.city" variant="outlined"></v-text-field><v-text-fieldv-model="data.form.email":label="$t('base.customer.email')":rules="data.rules.email"variant="outlined"></v-text-field><v-text-fieldv-model="data.form.address":label="$t('base.customer.address')":rules="data.rules.address"variant="outlined"></v-text-field><v-text-fieldv-model="data.form.manager":label="$t('base.customer.manager')":rules="data.rules.manager"variant="outlined"></v-text-field><v-text-fieldv-model="data.form.contact_tel":label="$t('base.customer.contact_tel')":rules="data.rules.contact_tel"variant="outlined"></v-text-field></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 { CustomerVO } from '@/types/Base/Customer'
import i18n from '@/languages/i18n'
import { hookComponent } from '@/components/system/index'
import { addCustomer, updateCustomer } from '@/api/base/customer'const formRef = ref()
const emit = defineEmits(['close', 'saveSuccess'])const props = defineProps<{showDialog: booleanform: CustomerVO
}>()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<CustomerVO>({id: 0,customer_name: '',city: '',address: '',manager: '',email: '',contact_tel: '',is_valid: true}),rules: {customer_name: [(val: string) => !!val || `${ i18n.global.t('system.checkText.mustInput') }${ i18n.global.t('base.customer.customer_name') }!`],city: [],address: [],manager: [],email: [],contact_tel: [],is_valid: []}
})const method = reactive({closeDialog: () => {emit('close')},submit: async () => {const { valid } = await formRef.value.validate()if (valid) {const { data: res } = dialogTitle.value === 'add' ? await addCustomer(data.form) : await updateCustomer(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) {data.form = props.form}}
)
</script><style scoped lang="less">
.v-form {div {margin-bottom: 7px;}
}
</style>
3.2 接口代码
[Route("customer")][ApiController][ApiExplorerSettings(GroupName = "Base")]public class CustomerController : BaseController{#region Args/// <summary>/// customer Service/// </summary>private readonly ICustomerService _customerService;/// <summary>/// Localizer Service/// </summary>private readonly IStringLocalizer<ModernWMS.Core.MultiLanguage> _stringLocalizer;#endregion#region constructor/// <summary>/// constructor/// </summary>/// <param name="customerService">customer Service</param>/// <param name="stringLocalizer">Localizer</param>public CustomerController(ICustomerService customerService, IStringLocalizer<ModernWMS.Core.MultiLanguage> stringLocalizer){this._customerService = customerService;this._stringLocalizer = stringLocalizer;}#endregion#region Api/// <summary>/// page search/// </summary>/// <param name="pageSearch">args</param>/// <returns></returns>[HttpPost("list")]public async Task<ResultModel<PageData<CustomerViewModel>>> PageAsync(PageSearch pageSearch){var (data, totals) = await _customerService.PageAsync(pageSearch, CurrentUser);return ResultModel<PageData<CustomerViewModel>>.Success(new PageData<CustomerViewModel>{Rows = data,Totals = totals});}/// <summary>/// Get all records/// </summary>/// <returns>args</returns>[HttpGet("all")]public async Task<ResultModel<List<CustomerViewModel>>> GetAllAsync(){var data = await _customerService.GetAllAsync(CurrentUser);if (data.Any()){return ResultModel<List<CustomerViewModel>>.Success(data);}else{return ResultModel<List<CustomerViewModel>>.Success(new List<CustomerViewModel>());}}/// <summary>/// get a record by id/// </summary>/// <returns>args</returns>[HttpGet]public async Task<ResultModel<CustomerViewModel>> GetAsync(int id){var data = await _customerService.GetAsync(id);if (data != null && data.id > 0){return ResultModel<CustomerViewModel>.Success(data);}else{return ResultModel<CustomerViewModel>.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(CustomerViewModel viewModel){var (id, msg) = await _customerService.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(CustomerViewModel viewModel){var (flag, msg) = await _customerService.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 _customerService.DeleteAsync(id);if (flag){return ResultModel<string>.Success(msg);}else{return ResultModel<string>.Error(msg);}}#endregion#region Import/// <summary>/// import customers by excel/// </summary>/// <param name="input">excel data</param>/// <returns></returns>[HttpPost("excel")]public async Task<ResultModel<List<CustomerImportViewModel>>> ExcelAsync(List<CustomerImportViewModel> input){var (flag, errorData) = await _customerService.ExcelAsync(input, CurrentUser);if (flag){return ResultModel<List<CustomerImportViewModel>>.Success(errorData);}else{return ResultModel<List<CustomerImportViewModel>>.Error("", 400, errorData);}}#endregion}