index.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. <!-- 交易服务-出境检测-提交申请/修改 -->
  2. <template>
  3. <app-drawer class="customs-exit-edit" title="出境申请" :width="960" v-model:show="show" :loading="loading"
  4. :refresh="refresh">
  5. <div class="customs-exit-edit__info">
  6. <p>*若货物由广州钻石交易中心经快递发送,快递员揽件时即视为委托方已收货,广州钻石交易中心不承担后续货物安全责任。</p>
  7. <p>本表将作为委托方委托广州钻石交易中心办理出境检测业务申请使用,敬请务必如实填写公司信息、货物资料,并盖章确认。广州钻石交易中心根据此表确认信息,作为接受委托办理该批货物出境检测业务的凭证,仅对数量负责。</p>
  8. </div>
  9. <el-form ref="formRef" class="el-form--horizontal" label-width="170px" :model="formData" :rules="formRules">
  10. <el-form-item :class="formData.GZCJAccountType === 2 ? 'el-form-item--row' : ''" label="用户类型"
  11. prop="GZCJAccountType">
  12. <el-radio-group v-model="formData.GZCJAccountType" @change="clearList">
  13. <el-radio :label="item.value" v-for="(item, index) in gzcjAccountTypeList" :key="index">
  14. {{ item.label }}
  15. </el-radio>
  16. </el-radio-group>
  17. </el-form-item>
  18. <el-form-item label="送检账户" prop="GZCJAccount" v-if="formData.GZCJAccountType === 1">
  19. <el-input placeholder="请输入" v-model="formData.GZCJAccount" />
  20. </el-form-item>
  21. <el-form-item label="公司名称(中文)" prop="CompanyNameCN">
  22. <el-input placeholder="请输入" v-model="formData.CompanyNameCN" />
  23. </el-form-item>
  24. <el-form-item label="公司名称(英文)" prop="CompanyNameEn">
  25. <el-input placeholder="请输入" v-model="formData.CompanyNameEn" />
  26. </el-form-item>
  27. <el-form-item class="el-form-item--row" label="地址(中文)" prop="AddressCN">
  28. <el-input placeholder="请输入" v-model="formData.AddressCN" />
  29. </el-form-item>
  30. <el-form-item class="el-form-item--row" label="地址(英文)" prop="AddressEN">
  31. <el-input placeholder="请输入" v-model="formData.AddressEN" />
  32. </el-form-item>
  33. <el-form-item label="联系人姓名" prop="ContactName">
  34. <el-input placeholder="请输入" v-model="formData.ContactName" />
  35. </el-form-item>
  36. <el-form-item label="联系人职位" prop="ContactPosition">
  37. <el-input placeholder="请输入" v-model="formData.ContactPosition" />
  38. </el-form-item>
  39. <el-form-item label="联系人电话" prop="ContactPhoneNo">
  40. <el-input placeholder="请输入" v-model="formData.ContactPhoneNo" />
  41. </el-form-item>
  42. <el-form-item label="邮箱" prop="Email">
  43. <el-input placeholder="请输入" v-model="formData.Email" />
  44. </el-form-item>
  45. <el-form-item label="货物品类" prop="GZCJCategoryType">
  46. <el-select v-model="formData.GZCJCategoryType">
  47. <el-option :label="item.label" :value="item.value" v-for="(item, index) in getGZCJCategoryTypeList()"
  48. :key="index" />
  49. </el-select>
  50. </el-form-item>
  51. <el-form-item label="成品钻石加工国" prop="ProcessingCountry">
  52. <el-input placeholder="请输入" v-model="formData.ProcessingCountry" />
  53. </el-form-item>
  54. <el-form-item label="天然钻石毛坯原产地" prop="ZSOrigin">
  55. <el-input placeholder="请输入" v-model="formData.ZSOrigin" />
  56. </el-form-item>
  57. <el-form-item label="完成检测后收货方式" prop="GZCJDeliveryType">
  58. <el-select v-model="formData.GZCJDeliveryType">
  59. <el-option :label="item.label" :value="item.value" v-for="(item, index) in getGZCJDeliveryTypeList()"
  60. :key="index" />
  61. </el-select>
  62. </el-form-item>
  63. </el-form>
  64. <div class="customs-exit-edit__table"
  65. v-if="formData.GZCJAccountType === 2 ? !!formData.GZCJCategoryType : !!formData.GZCJAccountType">
  66. <app-table :data="formData.GZCJCategoryDetails" :columns="columns" :max-height="400" border>
  67. <template #toolbar>
  68. <el-upload accept=".xlsx,.xls" :show-file-list="false" :auto-upload="false" @change="handleExcel">
  69. <el-button size="small">导入</el-button>
  70. </el-upload>
  71. <el-button size="small" @click="openEdit()">新增</el-button>
  72. <el-button size="small" @click="clearList()">清空</el-button>
  73. </template>
  74. <!-- 形状 -->
  75. <template #GZCJShapeType="{ value }">
  76. {{ getGZCJShapeTypeName(value) }}
  77. </template>
  78. <!-- 刻印服务(证书号/ 其他/ 无) -->
  79. <template #GZCJMarkType="{ value }">
  80. {{ getGZCJMarkTypeName(value) }}
  81. </template>
  82. <!-- 是否披露处理 -->
  83. <template #GZCJPublishType="{ value }">
  84. {{ getGZCJPublishTypeName(value) }}
  85. </template>
  86. <!-- 服务类别 -->
  87. <template #GZCJServiceType="{ value }">
  88. {{ getGZCJServiceTypeName(value) }}
  89. </template>
  90. <!-- 操作 -->
  91. <template #operate="{ row, index }">
  92. <el-button-group size="small">
  93. <el-button @click="openEdit(row)">修改</el-button>
  94. <el-button @click="deleteRecord(index)">删除</el-button>
  95. </el-button-group>
  96. </template>
  97. </app-table>
  98. </div>
  99. <template #footer>
  100. <el-button @click="onCancel(false)" plain>取消</el-button>
  101. <el-button type="primary" @click="onSubmit(true)">保存草稿</el-button>
  102. <el-button type="primary" @click="onSubmit()">提交申请</el-button>
  103. </template>
  104. <component :is="componentMap.get(componentId)" v-bind="{ formData, detail }" @update="onUpdate"
  105. @closed="closeComponent" v-if="componentId" />
  106. </app-drawer>
  107. </template>
  108. <script lang="ts" setup>
  109. import { ref, computed, onMounted, defineAsyncComponent, PropType } from 'vue'
  110. import { ElMessage } from 'element-plus'
  111. import type { FormInstance, FormRules, UploadFile } from 'element-plus'
  112. import { read, utils } from 'xlsx'
  113. import { useComponent } from '@/hooks/component'
  114. import { validateRules } from '@/constants/regex'
  115. import { enumStore } from '@/stores'
  116. import { getGZCJAccountTypeList, getGZCJCategoryTypeList, getGZCJDeliveryTypeList, getGZCJShapeTypeName, getGZCJPublishTypeName, getGZCJMarkTypeName, getGZCJServiceTypeName } from '@/constants/customs'
  117. import { useCJJCOrderApply } from '@/business/customs/exit'
  118. import AppDrawer from '@pc/components/base/drawer/index.vue'
  119. import AppTable from '@pc/components/base/table/index.vue'
  120. const props = defineProps({
  121. selectedRow: {
  122. type: Object as PropType<Ermcp.GZCJJCOrderRsp>
  123. }
  124. })
  125. const componentMap = new Map<string, unknown>([
  126. ['detailEdit', defineAsyncComponent(() => import('./detail-edit/index.vue'))],
  127. ])
  128. const { componentId, openComponent, closeComponent } = useComponent()
  129. const { loading, formData, formSubmit } = useCJJCOrderApply(props.selectedRow)
  130. const show = ref(true)
  131. const refresh = ref(false)
  132. const formRef = ref<FormInstance>()
  133. const gzcjAccountTypeList = getGZCJAccountTypeList()
  134. const detail = ref<Proto.GZCJCategoryDetail>() // 当前选择的货物明细
  135. const columns = computed<Ermcp.TableColumn[]>(() => {
  136. switch (formData.GZCJAccountType) {
  137. case 1: {
  138. return [
  139. { prop: 'GZNo', label: '货物编号' },
  140. { prop: 'GZCJShapeType', label: '形状', width: 200 },
  141. { prop: 'Weight', label: '重量ct' },
  142. { prop: 'Amount', label: '参考货值USD' },
  143. { prop: 'ColorInfo', label: '彩钻信息' },
  144. { prop: 'Remark', label: '其他' },
  145. { prop: 'operate', label: '操作', width: 160, fixed: 'right' }
  146. ]
  147. }
  148. case 2: {
  149. return [
  150. { prop: 'GZNo', label: '货物编号' },
  151. { prop: 'GZCJShapeType', label: '形状', width: 200 },
  152. { prop: 'Weight', label: '重量ct' },
  153. { prop: 'Amount', label: '参考货值USD' },
  154. { prop: 'GZCJMarkType', label: '刻印服务(证书号/ 其他/ 无)', width: 220 },
  155. { prop: 'GZCJPublishType', label: '是否披露处理' },
  156. { prop: 'GZCJServiceType', label: '服务类别' },
  157. { prop: 'OriginCertNo', label: '原证书号' },
  158. { prop: 'ColorInfo', label: '彩钻信息' },
  159. { prop: 'Remark', label: '其他' },
  160. { prop: 'operate', label: '操作', width: 160, fixed: 'right' }
  161. ]
  162. }
  163. }
  164. return []
  165. })
  166. const formRules: FormRules = {
  167. GZCJAccountType: [{
  168. required: true,
  169. message: '请选择用户类型'
  170. }],
  171. GZCJAccount: [{
  172. required: true,
  173. message: '请输入送检账户'
  174. }],
  175. CompanyNameCN: [{
  176. required: true,
  177. message: '请输入公司名称(中文)'
  178. }],
  179. CompanyNameEn: [{
  180. required: true,
  181. message: '请输入公司名称(英文)'
  182. }],
  183. AddressCN: [{
  184. required: true,
  185. message: '请输入地址(中文)'
  186. }],
  187. AddressEN: [{
  188. required: true,
  189. message: '请输入地址(英文)'
  190. }],
  191. ContactName: [{
  192. required: true,
  193. message: '请输入联系人姓名'
  194. }],
  195. ContactPosition: [{
  196. required: true,
  197. message: '请输入联系人职位'
  198. }],
  199. ContactPhoneNo: [{
  200. required: true,
  201. trigger: 'blur',
  202. validator: (rule, value, callback) => {
  203. if (value) {
  204. if (validateRules.phone.validate(value)) {
  205. callback()
  206. } else {
  207. callback(new Error(validateRules.phone.message))
  208. }
  209. } else {
  210. callback(new Error('请输入联系人电话'))
  211. }
  212. }
  213. }],
  214. Email: [{
  215. required: true,
  216. trigger: 'blur',
  217. validator: (rule, value, callback) => {
  218. if (value) {
  219. if (validateRules.email.validate(value)) {
  220. callback()
  221. } else {
  222. callback(new Error(validateRules.email.message))
  223. }
  224. } else {
  225. callback(new Error('请输入邮箱'))
  226. }
  227. }
  228. }],
  229. GZCJCategoryType: [{
  230. required: true,
  231. message: '请选择货物品类'
  232. }],
  233. ProcessingCountry: [{
  234. required: true,
  235. message: '请输入成品钻石加工国'
  236. }],
  237. ZSOrigin: [{
  238. required: true,
  239. message: '请输入天然钻石毛坯原产地'
  240. }],
  241. GZCJDeliveryType: [{
  242. required: true,
  243. message: '请选择完成检测后收货方式'
  244. }],
  245. }
  246. // 打开编辑
  247. const openEdit = (row?: Proto.GZCJCategoryDetail) => {
  248. detail.value = row
  249. openComponent('detailEdit')
  250. }
  251. // 清空列表数据
  252. const clearList = () => {
  253. formData.GZCJCategoryDetails = []
  254. }
  255. // 删除列表数据
  256. const deleteRecord = (index: number) => {
  257. formData.GZCJCategoryDetails?.splice(index, 1)
  258. formData.GZCJCategoryDetails?.forEach((e, i) => e.OrderIndex = i + 1) // 重置序列
  259. }
  260. // 处理导入的excel表格
  261. const handleExcel = (uploadFile: UploadFile) => {
  262. const fileReader = new FileReader()
  263. fileReader.readAsBinaryString(uploadFile.raw as Blob)
  264. fileReader.onload = (ev) => {
  265. const data = ev.target?.result
  266. if (data) {
  267. const { getEnumTypeList } = enumStore.actions
  268. const shapeTypeList = getEnumTypeList('GZCJShapeType')
  269. const markTypeList = getEnumTypeList('GZCJMarkType')
  270. const publishType = getEnumTypeList('GZCJPublishType')
  271. const serviceType = getEnumTypeList('GZCJServiceType')
  272. const workbook = read(data, { type: 'binary' })
  273. const wsname = workbook.SheetNames[0]
  274. const list = utils.sheet_to_json(workbook.Sheets[wsname], { header: ['Index', 'GZNo', 'GZCJShapeType', 'Weight', 'Amount', 'GZCJMarkType', 'GZCJPublishType', 'GZCJServiceType', 'OriginCertNo', 'ColorInfo', 'Remark'] })
  275. clearList()
  276. list.forEach((row, index) => {
  277. if (index > 0) {
  278. const item: Proto.GZCJCategoryDetail = {
  279. OrderIndex: 0, // 顺序,必填
  280. GZNo: '', // 货物编号,必填
  281. GZCJShapeType: 0, // 形状,必填
  282. Weight: 0, // 重量(CT),3位小数,必填
  283. Amount: 0, // 参考货值(USD),2位小数,必填
  284. ColorInfo: '', // 彩钻信息
  285. Remark: '', // 备注
  286. GZCJMarkType: 0,// 刻印服务,必填
  287. GZCJPublishType: 0,// 是否披露处理,必填
  288. GZCJServiceType: 0, // 服务类别,必填
  289. OriginCertNo: '', // 原证书号
  290. }
  291. Object.entries(row as { [key: string]: unknown }).forEach(([key, value], index) => {
  292. if (index > 0 && Reflect.has(item, key)) {
  293. let enumType
  294. switch (key) {
  295. case 'GZCJShapeType': {
  296. enumType = shapeTypeList.find((e) => e.label === value)
  297. break
  298. }
  299. case 'GZCJMarkType': {
  300. enumType = markTypeList.find((e) => e.label === value)
  301. break
  302. }
  303. case 'GZCJPublishType': {
  304. enumType = publishType.find((e) => e.label === value)
  305. break
  306. }
  307. case 'GZCJServiceType': {
  308. enumType = serviceType.find((e) => e.label === value)
  309. break
  310. }
  311. default: {
  312. enumType = { value }
  313. }
  314. }
  315. Object.defineProperty(item, key, { value: enumType?.value ?? 0 })
  316. }
  317. })
  318. formData.GZCJCategoryDetails?.push(item)
  319. }
  320. })
  321. }
  322. }
  323. }
  324. // 更新列表数据
  325. const onUpdate = (item: Proto.GZCJCategoryDetail) => {
  326. const { GZCJCategoryDetails = [] } = formData
  327. const index = GZCJCategoryDetails.findIndex((e) => e.OrderIndex === item.OrderIndex)
  328. if (index > -1) {
  329. GZCJCategoryDetails[index] = item
  330. } else {
  331. item.OrderIndex = GZCJCategoryDetails.length + 1
  332. GZCJCategoryDetails.push(item)
  333. }
  334. }
  335. const onCancel = (isRefresh = false) => {
  336. show.value = false
  337. refresh.value = isRefresh
  338. }
  339. const onSubmit = (draft = false) => {
  340. formRef.value?.validate((valid) => {
  341. if (valid) {
  342. if (draft) {
  343. formData.ApplyType = props.selectedRow?.orderidstr ? 2 : 1
  344. } else {
  345. formData.ApplyType = 3
  346. }
  347. if (formData.GZCJCategoryDetails?.length) {
  348. formSubmit().then(() => {
  349. ElMessage.success('提交成功')
  350. onCancel(true)
  351. }).catch((err) => {
  352. ElMessage.error(err)
  353. })
  354. } else {
  355. ElMessage.warning('请添加批次')
  356. }
  357. }
  358. })
  359. }
  360. onMounted(() => {
  361. const { gzcjaccounttype = gzcjAccountTypeList[0]?.value } = props.selectedRow ?? {}
  362. formData.GZCJAccountType = gzcjaccounttype
  363. })
  364. </script>
  365. <style lang="less">
  366. @import './index.less';
  367. </style>