index.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  1. <template>
  2. <div class="h-full flex">
  3. <div class="p-5 w-80 pr-0">
  4. <div class="bg-white w-full h-full shadow-md rounded-md flex flex-col">
  5. <div class="flex-1 p-3 overflow-y-auto">
  6. <!-- 筛选条件 -->
  7. <el-form :model="filterForm" label-width="70px" style="max-width: 600px">
  8. <template v-for="(item, index) in filterItems">
  9. <el-form-item :label="item.label" v-if="item.type != 'date'">
  10. <el-input v-if="item.type === 'input'" v-model="filterForm[item.key as keyof FilterForm]" clearable
  11. placeholder="请输入"></el-input>
  12. <el-select v-else v-model="filterForm[item.key as keyof FilterForm]" placeholder="请选择" clearable>
  13. <el-option v-for="option in item.options" :key="option.id" :label="option.name" :value="option.id" />
  14. </el-select>
  15. </el-form-item>
  16. <template v-if="item.type == 'date'">
  17. <el-form-item :label="item.label">
  18. <el-date-picker v-model="filterForm.startTime" type="date" placeholder="请选择" :clearable="false"
  19. format="YYYY-MM-DD" value-format="YYYY-MM-DD" />
  20. </el-form-item>
  21. <el-form-item label="">
  22. <el-date-picker v-model="filterForm.endTime" type="date" placeholder="请选择" :clearable="false"
  23. format="YYYY-MM-DD" value-format="YYYY-MM-DD" />
  24. </el-form-item>
  25. </template>
  26. </template>
  27. </el-form>
  28. </div>
  29. <div class="w-full flex p-3 shadow-[0_-3px_5px_0px_rgba(0,0,0,0.2)]">
  30. <El-button class="w-full" @click="resetFilterForm()">重置</El-Button>
  31. <El-button type="primary" class="w-full" @click="getTableList()">搜索</El-Button>
  32. </div>
  33. </div>
  34. </div>
  35. <div class="flex-1 p-5 overflow-auto">
  36. <div class="bg-white w-full h-full p-3 shadow-md rounded-md flex flex-col">
  37. <div class="flex justify-end pb-3">
  38. <!-- 操作按钮 -->
  39. <el-button type="primary" @click="editOrder(false)">新建订单</el-button>
  40. <el-button type="primary" :disabled="batchTableData.length <= 0">批量转移</el-button>
  41. <el-button type="primary" @click="batchDeteleItem()" :disabled="batchTableData.length <= 0">批量删除</el-button>
  42. <el-button type="primary" @click="showVisible('deteleOrderVisible')">回收站</el-button>
  43. <el-button type="primary" @click="showVisible('importVisible')">导入</el-button>
  44. <el-button type="primary" @click="exportOrderTableList()" :loading="allLoading.exoprtLoading">导出</el-button>
  45. </div>
  46. <div class="flex-1 w-full overflow-hidden">
  47. <!-- 表格 -->
  48. <el-table ref="otherTableRef" :data="formTable" border v-loading="allLoading.formTableLading"
  49. style="width: 100%;height: 100%;" @selection-change="changeBatch">
  50. <el-table-column type="selection" width="55" />
  51. <el-table-column v-for="(column, index) in tableColumns" :key="index" :prop="column.prop"
  52. :label="column.label" :width="column.width">
  53. <template #default="scope">
  54. <template v-if="column.event === 'toDetali'">
  55. <el-button link type="primary" size="large" @click="toDetali(scope.row)">{{ scope.row[column.prop]
  56. }}</el-button>
  57. </template>
  58. </template>
  59. </el-table-column>
  60. <el-table-column :label="'操作'" :width="'200px'" fixed="right">
  61. <template #default="scope">
  62. <el-button link type="primary" size="large" @click="editOrder(scope.row)">编辑</el-button>
  63. <el-button link type="primary" size="large" @click="newTask(scope.row)">新建任务</el-button>
  64. <el-button link type="danger" size="large"
  65. @click="orderDeteleItem(scope.row.id, scope.row.orderName)">删除</el-button>
  66. </template>
  67. </el-table-column>
  68. </el-table>
  69. </div>
  70. <div class="flex justify-end pt-3">
  71. <!-- 分页 -->
  72. <el-pagination layout="total, prev, pager, next, sizes" :total="formTablePaging.total"
  73. :hide-on-single-page="true" @size-change="handleSizeChange" @current-change="handleCurrentChange" />
  74. </div>
  75. </div>
  76. </div>
  77. <!-- 弹窗 -->
  78. <el-dialog v-model="allVisible.editOrderVisible" width="1000" :show-close="false" top="10vh">
  79. <template #header="{ close, titleId, titleClass }">
  80. <div class="flex justify-between items-center border-b pb-3 dialog-header">
  81. <h4 :id="titleId">{{ allText.orderEditText }}</h4>
  82. <div>
  83. <el-button type="primary" :loading="allLoading.editSaveLading" @click="saveOrder(true)">保存并新建</el-button>
  84. <el-button type="primary" :loading="allLoading.editSaveLading" @click="saveOrder(false)">保存</el-button>
  85. <el-button @click="closeVisible('editOrderVisible')">取消</el-button>
  86. </div>
  87. </div>
  88. </template>
  89. <div class="h-[60vh] overflow-y-auto scroll-bar pt-3" v-loading="allLoading.orderTemplateLoadinng">
  90. <GenerateForm ref="orderTemplateRef" :data="orderTemplate" :key="orderTemplateKey" :value="orderTemplateValue" />
  91. <div>相关产品</div>
  92. <RelatedProducts ref="relatedProductsRef" :productTableList="productTableList"
  93. :productTableListValue="productTableListValue" />
  94. </div>
  95. </el-dialog>
  96. <!-- 新建任务 -->
  97. <TaskModal :visible="allVisible.taskModalVisible" :edit-form="taskModalForm" :save-loading="taskLoading"
  98. @close="allVisible.taskModalVisible = false" @submit="submitForm" :title="'新建任务'"
  99. :disabled-list="['taskType', 'orderId']" />
  100. <!-- 回收站 -->
  101. <DeteleTables :visibles="allVisible.deteleOrderVisible" @closeVisible="closeVisible" />
  102. <!-- 导入 -->
  103. <el-dialog v-model="allVisible.importVisible" width="680" :show-close="false" top="10vh">
  104. <template #header="{ close, titleId, titleClass }">
  105. <div class="flex justify-between items-center border-b pb-3 dialog-header">
  106. <h4 :id="titleId">导入联系人</h4>
  107. <div class="flex">
  108. <el-upload class="upload-demo mr-3" :limit="1" :show-file-list="false" accept=".xlsx"
  109. :http-request="importBusiness">
  110. <el-button type="primary" :loading="allLoading.importLoading">导入</el-button>
  111. </el-upload>
  112. <el-button @click="allVisible.importVisible = false">取消</el-button>
  113. </div>
  114. </div>
  115. </template>
  116. <div class="p-8">
  117. <div class="ml-4 mr-4">
  118. <div class="flex items-center">1、点击下载 <el-link type="primary"
  119. @click="downloadTemplate(IMPORTMOD, allText.importText)">{{ allText.importText }}</el-link></div>
  120. <div class="mt-4">2、填写excel文件、订单名称、客户名称、订单金额、负责人必填</div>
  121. </div>
  122. </div>
  123. </el-dialog>
  124. </div>
  125. </template>
  126. <script lang="ts" setup>
  127. import { ref, reactive, onMounted, inject, defineExpose } from "vue";
  128. import { getAllListByCode, getFromValue, resetFromValue, getFirstDayOfMonth, getLastDayOfMonth, formatDate, getTemplateKey, createTaskFromType, confirmAction, downloadFile, downloadTemplate } from '@/utils/tools'
  129. import { post, get, uploadFile } from "@/utils/request";
  130. import { tableColumns, GETSYSFILED, GETPERSONNEL, GETGENERATEFOEM, MOD, GETTABLELIST, GETALLPRODUCT, GETTABLELISTPRODUCT, URL_OEDERUPDATE, URL_PRODUTWITHORDER, URL_DETELEITEM, EXPORTTIME, IMPORTMOD, IMPORITEM } from "./api";
  131. import { useRouter, useRoute } from "vue-router";
  132. import { GenerateForm } from '@zmjs/form-design';
  133. import { formatDateTime } from "@/utils/times";
  134. import { ElTable, UploadRequestOptions } from "element-plus";
  135. import { createTask } from "@/components/TaskModal/taskFunction";
  136. import { URL_FETALL } from "../customer/api";
  137. import RelatedProducts from '@/components/relatedProducts/relatedProducts.vue'
  138. import DeteleTables from './component/deteleTables.vue'
  139. import TaskModal from '@/components/TaskModal/index.vue'
  140. const router = useRouter()
  141. const globalPopup = inject<GlobalPopup>('globalPopup')
  142. const filterForm = reactive<FilterForm>({ // 筛选条件 Value
  143. contactPerson: "",
  144. customerId: "",
  145. phoneNumber: '',
  146. responsibleId: '',
  147. createId: '',
  148. email: '',
  149. startTime: getFirstDayOfMonth(new Date()),
  150. endTime: formatDate(new Date())
  151. });
  152. const selectData = reactive({ // 下拉数据
  153. Personnel: [] as personnelInterface[],
  154. Customer: [] as any[], // 客户名称
  155. OrderType: [] as any[], // 订单类型
  156. RemittanceStatus: [{ id: 0, name: '已回款' }, { id: 1, name: '未回款' }, { id: 2, name: '已完全回款' }] as any[], // 回款状态
  157. AllProduct: [] as any[] // 所有产品
  158. })
  159. const formTablePaging = reactive({ // 分页条件
  160. pageIndex: 1,
  161. pageSize: 10,
  162. total: 0,
  163. })
  164. const allLoading = reactive({ // 按钮加载 Loading
  165. formTableLading: false,
  166. editSaveLading: false,
  167. orderTemplateLoadinng: false,
  168. exoprtLoading: false,
  169. importLoading: false
  170. })
  171. const allVisible = reactive({
  172. editOrderVisible: false,
  173. taskModalVisible: false,
  174. deteleOrderVisible: false,
  175. importVisible: false
  176. })
  177. const allText = reactive({
  178. orderEditText: '新建订单',
  179. importText: '销售订单表导出.xlsx'
  180. })
  181. const orderTemplate = ref({
  182. list: [],
  183. config: {}
  184. })
  185. const filterItems = ref<FilterItem[]>([
  186. { label: '订单编号', key: 'orderCode', type: 'input' },
  187. { label: '订单名称', key: 'orderName', type: 'input' },
  188. { label: '客户名称', key: 'customId', type: 'select', options: selectData.Customer },
  189. { label: '商机名称', key: 'businessOpportunityId', type: 'input' },
  190. { label: '订单类型', key: 'ordertype', type: 'select', options: selectData.OrderType },
  191. { label: '回款状态', key: 'receivedStatus', type: 'select', options: selectData.RemittanceStatus },
  192. { label: '负责人', key: 'inchargerId', type: 'select', options: selectData.Personnel },
  193. { label: '下单时间', key: '', type: 'date' },
  194. ]) // 渲染筛选条件
  195. const orderTemplateValue = ref({})
  196. const orderTemplateKey = ref(1)
  197. const orderTemplateRef = ref<typeof GenerateForm>()
  198. const relatedProductsRef = ref<typeof RelatedProducts>()
  199. const otherTableRef = ref<InstanceType<typeof ElTable>>()
  200. const taskLoading = ref<saveLoadingType>('1')
  201. const batchTableData = ref([])
  202. const formTable = ref([]) // 表格数据
  203. const productTableList = ref([])
  204. const productTableListValue = ref([])
  205. const taskModalForm = ref({})
  206. async function importBusiness(param: UploadRequestOptions) {
  207. allLoading.importLoading = true
  208. const formData = new FormData();
  209. formData.append('multipartFile', param.file)
  210. const res = await uploadFile(IMPORITEM, formData).finally(() => {
  211. allLoading.importLoading = false
  212. })
  213. if (res.code == 'ok') {
  214. globalPopup?.showSuccess('导入成功' || '')
  215. getTableList()
  216. return
  217. }
  218. globalPopup?.showError(res.msg || '')
  219. }
  220. function exportOrderTableList() {
  221. allLoading.exoprtLoading = true
  222. let valueForm = getFromValue(filterForm)
  223. post(EXPORTTIME, {...valueForm}).then((res) => {
  224. downloadFile(res.data, allText.importText)
  225. }).finally(() => {
  226. allLoading.exoprtLoading = false
  227. })
  228. }
  229. function batchDeteleItem() {
  230. const value = batchTableData.value.map((item: any) => item.id).join(',')
  231. const label = batchTableData.value.map((item: any) => item.orderName).join(',')
  232. orderDeteleItem(value, label, true)
  233. }
  234. function orderDeteleItem(value: string | number, label: string, batch: boolean = false) {
  235. confirmAction(`确定${batch ? '批量' : ''}删除【${label}】客户吗?`).then(() => {
  236. post(URL_DETELEITEM, { ids: value }).then(res => {
  237. if (res.code != 'ok') {
  238. globalPopup?.showError(res.msg)
  239. return
  240. }
  241. globalPopup?.showSuccess('删除成功')
  242. changeBatch(false)
  243. getTableList()
  244. }).catch((err) => {
  245. globalPopup?.showError(err.message)
  246. })
  247. })
  248. }
  249. function submitForm(submitData: any, isClose: boolean) {
  250. taskLoading.value = '2'
  251. createTask(submitData, isClose).then((res) => {
  252. const { saveLoading, isClose } = res
  253. taskLoading.value = saveLoading
  254. allVisible.taskModalVisible = isClose
  255. globalPopup?.showSuccess('新增成功')
  256. }).catch((err) => {
  257. const { saveLoading, isClose, message } = err
  258. taskLoading.value = saveLoading
  259. allVisible.taskModalVisible = isClose
  260. globalPopup?.showError(message)
  261. })
  262. }
  263. function newTask(item: any) {
  264. const { id } = item
  265. taskModalForm.value = { ...createTaskFromType(2), orderId: id, }
  266. allVisible.taskModalVisible = true
  267. }
  268. function saveOrder(flag: boolean) {
  269. orderTemplateRef.value?.getData().then((res: any) => {
  270. let productTableListData = relatedProductsRef?.value?.returnData()
  271. for (var i in productTableListData) {
  272. productTableListData[i].sealPrice = productTableListData[i].sellingPrice,
  273. productTableListData[i].discount = productTableListData[i].discount,
  274. productTableListData[i].num = productTableListData[i].quantity
  275. }
  276. const produt = productTableListData ? JSON.stringify(productTableListData) : []
  277. allLoading.editSaveLading = true
  278. post(URL_OEDERUPDATE, {
  279. ...orderTemplateValue.value,
  280. ...res,
  281. orderEndDate: res.orderEndDate ? formatDate(res.orderEndDate) : '',
  282. orderStartDate: res.orderStartDate ? formatDate(res.orderStartDate) : '',
  283. orderProductDetailString: produt
  284. }).then((_res) => {
  285. allVisible.editOrderVisible = flag
  286. globalPopup?.showSuccess('保存成功')
  287. if (flag) {
  288. orderTemplateRef.value?.reset()
  289. }
  290. getTableList()
  291. }).finally(() => {
  292. allLoading.editSaveLading = false
  293. })
  294. }).catch((_err: any) => {
  295. globalPopup?.showError('请填写完整')
  296. })
  297. }
  298. function editOrder(item: any) {
  299. showVisible('editOrderVisible')
  300. allLoading.orderTemplateLoadinng = true
  301. if (item) {
  302. editProduct(item)
  303. const templateKey = getTemplateKey(orderTemplate.value.list)
  304. let formVal: templateKey = { id: item.id }
  305. for (let i = 0; i < templateKey.length; i++) {
  306. const key = templateKey[i]
  307. formVal[key] = item[key]
  308. }
  309. orderTemplateValue.value = formVal
  310. console.log(formVal, '<============ 数据')
  311. allText.orderEditText = '编辑订单'
  312. }
  313. if (!item) {
  314. orderTemplateValue.value = {}
  315. productTableListValue.value = []
  316. allText.orderEditText = '新增订单'
  317. }
  318. setTimeout(() => {
  319. orderTemplateRef.value && orderTemplateRef.value.reset()
  320. orderTemplateKey.value++
  321. allLoading.orderTemplateLoadinng = false
  322. }, 1000)
  323. }
  324. function toDetali(row: any) {
  325. router.push({
  326. path: `${MOD}/detail`,
  327. query: { id: row.id }
  328. })
  329. }
  330. function getTableList() {
  331. const formValue = getFromValue(filterForm)
  332. const formPaging = { pageIndex: formTablePaging.pageIndex, pageSize: formTablePaging.pageSize }
  333. allLoading.formTableLading = true
  334. post(GETTABLELIST, { ...formValue, ...formPaging }).then(res => {
  335. const { total, record } = res.data
  336. formTable.value = record
  337. formTablePaging.total = total
  338. }).finally(() => {
  339. allLoading.formTableLading = false
  340. })
  341. }
  342. function resetFilterForm() {
  343. let newFilterForm = resetFromValue(filterForm, { startTime: getFirstDayOfMonth(new Date()), endTime: formatDate(new Date()) })
  344. Object.assign(filterForm, newFilterForm)
  345. getTableList()
  346. }
  347. function getAllProduct() {
  348. get(GETALLPRODUCT, { pageIndex: -1, pageSize: -1 }).then((res) => {
  349. const { record } = res.data
  350. selectData.AllProduct = record
  351. })
  352. }
  353. async function getSystemField() {
  354. const systemField = getAllListByCode(['订单类型'])
  355. for (let i in systemField) {
  356. const { data } = await get(`${GETSYSFILED}?code=${systemField[i]}`)
  357. for (let key of Object.keys(selectData)) {
  358. if (systemField[i] == key) {
  359. Object.assign(selectData, { [key]: data })
  360. }
  361. }
  362. }
  363. const { data: personnelData } = await post(GETPERSONNEL, {})
  364. selectData.Personnel = personnelData.map((item: any) => {
  365. const { id, name, phone, jobNumber } = item
  366. return { id, name, phone, jobNumber }
  367. })
  368. const { data: customerData } = await post(URL_FETALL, {})
  369. selectData.Customer = (customerData || []).map((item: any) => {
  370. const { id, customName } = item
  371. return { id, name: customName }
  372. })
  373. const res = await get(GETGENERATEFOEM)
  374. orderTemplate.value = JSON.parse(res.data[0].config)
  375. setFilterItems()
  376. }
  377. function changeBatch(flag: boolean = true) {
  378. if (flag) {
  379. batchTableData.value = otherTableRef.value && otherTableRef.value.getSelectionRows()
  380. } else {
  381. batchTableData.value = []
  382. otherTableRef.value && otherTableRef.value.clearSelection()
  383. }
  384. }
  385. function showVisible(type: keyof typeof allVisible) { // 显示弹窗
  386. allVisible[type] = true
  387. }
  388. function closeVisible(type: keyof typeof allVisible) {
  389. allVisible[type] = false
  390. }
  391. function setFilterItems() {
  392. filterItems.value = [
  393. { label: '订单编号', key: 'orderCode', type: 'input' },
  394. { label: '订单名称', key: 'orderName', type: 'input' },
  395. { label: '客户名称', key: 'customId', type: 'select', options: selectData.Customer },
  396. { label: '商机名称', key: 'businessOpportunityId', type: 'input' },
  397. { label: '订单类型', key: 'ordertype', type: 'select', options: selectData.OrderType },
  398. { label: '回款状态', key: 'receivedStatus', type: 'select', options: selectData.RemittanceStatus },
  399. { label: '负责人', key: 'inchargerId', type: 'select', options: selectData.Personnel },
  400. { label: '下单时间', key: '', type: 'date' },
  401. ]
  402. }
  403. function editProduct(row: any) {
  404. post(URL_PRODUTWITHORDER, { id: row.id }).then(({ data }) => {
  405. const list = data.map((item: any) => {
  406. const { id, productName, productCode, unit, unitName, typeName, type, price, inventory, orderProductDetail } = item
  407. return {
  408. id, productId: id, productName, productCode, unit, unitName, typeName, type, price, inventory,
  409. quantity: +orderProductDetail?.num,
  410. discount: +orderProductDetail?.discount,
  411. sellingPrice: +orderProductDetail?.sealPrice,
  412. totalPrice: +orderProductDetail?.totalPrice
  413. }
  414. })
  415. productTableListValue.value = list
  416. })
  417. }
  418. function getProductTableList() {
  419. post(GETTABLELISTPRODUCT, { pageIndex: -1, pageSize: -1 }).then((res) => {
  420. if (res.code == 'ok') {
  421. const { record, total } = res.data
  422. productTableList.value = record.map((item: any) => {
  423. const { id, productName, productCode, unit, unitName, typeName, type, price, inventory } = item
  424. return {
  425. id,
  426. productId: id,
  427. productName,
  428. productCode,
  429. unit,
  430. unitName,
  431. price,
  432. type,
  433. typeName,
  434. inventory,
  435. quantity: '',
  436. discount: '',
  437. totalPrice: ''
  438. }
  439. })
  440. }
  441. })
  442. }
  443. function handleSizeChange(val: number) {
  444. formTablePaging.pageIndex = 1
  445. formTablePaging.pageSize = val
  446. getTableList()
  447. }
  448. function handleCurrentChange(val: number) {
  449. formTablePaging.pageIndex = val
  450. getTableList()
  451. }
  452. onMounted(() => {
  453. getSystemField()
  454. getAllProduct()
  455. getTableList()
  456. getProductTableList()
  457. })
  458. </script>
  459. <style lang="scss" scoped></style>