前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【愚公系列】2023年02月 WMS智能仓储系统-015.基础设置(货主信息、运费设置、客户信息)

【愚公系列】2023年02月 WMS智能仓储系统-015.基础设置(货主信息、运费设置、客户信息)

作者头像
愚公搬代码
发布2023-03-16 17:21:12
3810
发布2023-03-16 17:21:12
举报
文章被收录于专栏:历史专栏历史专栏

文章目录


前言

基础设置主要分为以下几个模块:

  • 首页
  • 公司信息
  • 角色设置
  • 菜单设置
  • 用户管理
  • 商品类别设置
  • 商品管理
  • 供应商信息
  • 仓库设置
  • 货主信息
  • 运费设置
  • 客户信息
在这里插入图片描述
在这里插入图片描述

这边需要提下客户和货主的概念:客户是与仓储签订直接合同的甲方,货主是拥有货权的人。

实际情况下,货主会委托某人或某企业管理自己的货物,而这个管理人或企业会与仓储签订合同,使用仓储的软硬件资源。

货主是客户的甲方,客户是仓储的甲方(货主和客户也可能是同一人或企业)。

一、基础设置

1.货主信息

在这里插入图片描述
在这里插入图片描述

1.1 页面代码

1、主页面代码

代码语言:javascript
复制
<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-field
                      v-model="data.searchForm.goods_owner_name"
                      clearable
                      hide-details
                      density="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 -->
          <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 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 info
  showDialog: 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 paging
  handlePageChange: ref<VxePagerEvents.PageChange>(({ currentPage, pageSize }) => {
    data.tablePage.pageIndex = currentPage
    data.tablePage.pageSize = pageSize
    method.refresh()
  }),
  // Import Dialog
  openDialogImport: () => {
    data.showDialogImport = true
  },
  closeDialogImport: () => {
    data.showDialogImport = false
  },
  saveSuccessImport: () => {
    method.refresh()
    method.closeDialogImport()
  },
  sureSearch: () => {
    data.tablePage.searchObjects = setSearchObject(data.searchForm)
    method.getOwnerOfCargoList()
  },
  // Find Data by Pagination
  getOwnerOfCargoList: async () => {
    const { data: res } = await getOwnerOfCargoByPage(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,
      goods_owner_name: '',
      city: '',
      address: '',
      contact_tel: '',
      manager: ''
    }
    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.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 table
  exportTable: () => {
    const $table = xTable.value
    exportData({
      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,
  () => {
    // 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、窗体代码

代码语言:javascript
复制
<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-field
              v-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-field
              v-model="data.form.address"
              :label="$t('base.ownerOfCargo.address')"
              :rules="data.rules.address"
              variant="outlined"
            ></v-text-field>
            <v-text-field
              v-model="data.form.manager"
              :label="$t('base.ownerOfCargo.manager')"
              :rules="data.rules.manager"
              variant="outlined"
            ></v-text-field>
            <v-text-field
              v-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: boolean
  form: 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 接口代码

代码语言:javascript
复制
[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、主页面代码

代码语言:javascript
复制
<!-- 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-field
                          v-model="data.searchForm.carrier"
                          clearable
                          hide-details
                          density="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-field
                          v-model="data.searchForm.departure_city"
                          clearable
                          hide-details
                          density="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-field
                          v-model="data.searchForm.arrival_city"
                          clearable
                          hide-details
                          density="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 -->
              <div
                class="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 add
  add: () => {
    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 dialog
  closeDialog: () => {
    data.showDialog = false
  },
  // After add or update success.
  saveSuccess: () => {
    method.refresh()
    method.closeDialog()
  },
  // Import Dialog
  openDialogImport: () => {
    data.showDialogImport = true
  },
  closeDialogImport: () => {
    data.showDialogImport = false
  },
  saveSuccessImport: () => {
    method.refresh()
    method.closeDialogImport()
  },
  // Refresh data
  refresh: () => {
    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.rows
    data.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 = currentPage
    data.tablePage.pageSize = pageSize

    method.getFreightList()
  }),
  exportTable: () => {
    const $table = xTable.value
    exportData({
      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,
  () => {
    // 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、窗体代码

代码语言:javascript
复制
<!-- 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-field
              v-model="data.form.carrier"
              :label="$t('base.freightSetting.carrier')"
              :rules="data.rules.carrier"
              variant="outlined"
            ></v-text-field>
            <v-text-field
              v-model="data.form.departure_city"
              :label="$t('base.freightSetting.departure_city')"
              :rules="data.rules.departure_city"
              variant="outlined"
            ></v-text-field>
            <v-text-field
              v-model="data.form.arrival_city"
              :label="$t('base.freightSetting.arrival_city')"
              :rules="data.rules.arrival_city"
              variant="outlined"
            ></v-text-field>
            <v-text-field
              v-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-field
              v-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-field
              v-model="data.form.min_payment"
              :label="$t('base.freightSetting.min_payment')"
              :rules="data.rules.min_payment"
              variant="outlined"
            ></v-text-field>
            <v-switch
              v-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: boolean
  form: 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 接口代码

代码语言:javascript
复制
     [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、主页面代码

代码语言:javascript
复制
<!-- 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-field
                      v-model="data.searchForm.customer_name"
                      clearable
                      hide-details
                      density="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 -->
          <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="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 Dialog
  openDialogImport: () => {
    data.showDialogImport = true
  },
  closeDialogImport: () => {
    data.showDialogImport = false
  },
  saveSuccessImport: () => {
    method.refresh()
    method.closeDialog()
  },
  sureSearch: () => {
    data.tablePage.searchObjects = setSearchObject(data.searchForm)
    method.getData()
  },
  // Add user
  add: () => {
    data.dialogForm = {
      id: 0,
      customer_name: '',
      city: '',
      address: '',
      manager: '',
      email: '',
      contact_tel: '',
      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.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 = currentPage
    data.tablePage.pageSize = pageSize
    method.getData()
  }),
  exportTable: () => {
    const $table = xTable.value
    exportData({
      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.rows
    data.tablePage.total = res.data.totals
  }
})
onMounted(() => {
  method.getData()
})

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、窗体代码

代码语言:javascript
复制
<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-field
              v-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-field
              v-model="data.form.email"
              :label="$t('base.customer.email')"
              :rules="data.rules.email"
              variant="outlined"
            ></v-text-field>
            <v-text-field
              v-model="data.form.address"
              :label="$t('base.customer.address')"
              :rules="data.rules.address"
              variant="outlined"
            ></v-text-field>
            <v-text-field
              v-model="data.form.manager"
              :label="$t('base.customer.manager')"
              :rules="data.rules.manager"
              variant="outlined"
            ></v-text-field>
            <v-text-field
              v-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: boolean
  form: 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 接口代码

代码语言:javascript
复制
    [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
    }
在这里插入图片描述
在这里插入图片描述
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2023-02-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 前言
  • 一、基础设置
    • 1.货主信息
      • 1.1 页面代码
      • 1.2 接口代码
    • 2.运费设置
      • 2.1 页面代码
      • 2.2 接口代码
    • 3.客户信息
      • 3.1 页面代码
      • 3.2 接口代码
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档