kanbanView.vue 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. <script lang="ts" setup>
  2. import { ref, reactive, onMounted, inject } from "vue";
  3. import { useRouter, useRoute } from "vue-router";
  4. import { VueDraggable } from 'vue-draggable-plus';
  5. import { MOD, OBTAIN_KANBAN_VIEW_DATA, PANEL_MOBILE_DATA, URL_STAGEIDNEXT } from '../api'
  6. import { Loading, MoreFilled } from '@element-plus/icons-vue'
  7. import { post, get, uploadFile } from "@/utils/request";
  8. import SvgIcon from "@/components/svgIcon/index.vue";
  9. import { formatDate, formatDateTime } from "@/utils/times";
  10. const emit = defineEmits()
  11. const router = useRouter()
  12. const viewList = ref<viewListInterface[]>([])
  13. const excessiveData = ref<any>({})
  14. const selectionStage = ref<number>()
  15. const switchingStagesVisable = ref(false)
  16. const allLoading = reactive({
  17. kanbanViewLoading: false,
  18. switchingStagesLoading: false
  19. })
  20. onMounted(() => {
  21. getKanbanViewData();
  22. })
  23. function promotionStage() {
  24. const item = viewList.value.find((item: any) => item.id == selectionStage.value)
  25. const { id: stageId, label: stageValue } = item as any
  26. allLoading.switchingStagesLoading = true
  27. post(URL_STAGEIDNEXT, { id: excessiveData.value.id, stageId, stageValue }).then(() => {
  28. switchingStagesVisable.value = false
  29. getKanbanViewData()
  30. }).finally(() => {
  31. allLoading.switchingStagesLoading = false
  32. })
  33. }
  34. function switchingStages(row: any) {
  35. excessiveData.value = row
  36. switchingStagesVisable.value = true
  37. }
  38. function onChange(e: any) {
  39. const data = {
  40. id: e.data.id,
  41. oldIndex: e.oldIndex,
  42. newIndex: e.newIndex,
  43. oldStagesId: e.from.id,
  44. newStagesId: e.to.id
  45. }
  46. setDataLoading(viewList.value, data.newStagesId, data.id, true)
  47. post(PANEL_MOBILE_DATA, { ...data }).then(() => {
  48. getKanbanViewData()
  49. }).finally(() => {
  50. setDataLoading(viewList.value, data.newStagesId, data.id, false)
  51. })
  52. }
  53. function setDataLoading(list: any, targetLevel_1Id: number | string, targetLevel_2Id: number | string, flag: boolean) {
  54. for (let i = 0; i < list.length; i++) {
  55. if (list[i].id == targetLevel_1Id) {
  56. for (let j = 0; j < list[i].list.length; j++) {
  57. if (list[i].list[j].id == targetLevel_2Id) {
  58. list[i].list[j].loadData = flag;
  59. }
  60. }
  61. }
  62. }
  63. }
  64. function selectData(_value: boolean, _row: any) {
  65. let data = [...viewList.value].flatMap(item =>
  66. item.list.filter(subItem => subItem.multipleChoice)
  67. );
  68. let newData = JSON.parse(JSON.stringify(data))
  69. newData.forEach((item: any) => {
  70. delete item.multipleChoice
  71. delete item.loadData
  72. });
  73. emit('kanbanViewClick', 'multipleChoice', newData)
  74. }
  75. function deteleItem(row: any) {
  76. emit('kanbanViewClick', 'delete', row)
  77. }
  78. function editItem(row: any) {
  79. emit('kanbanViewClick', 'edit', row)
  80. }
  81. function addTaskItem(row: any) {
  82. emit('kanbanViewClick', 'addTask', row)
  83. }
  84. function toDetailPath(row: any) {
  85. router.push({
  86. path: `${MOD}/detail`,
  87. query: { id: row.id }
  88. })
  89. }
  90. function searchDashboardView(row: businessOpportunityFormType) {
  91. getKanbanViewData(row || {})
  92. }
  93. function getKanbanViewData(formVal: any = {}) { // 获取看板视图数据
  94. allLoading.kanbanViewLoading = true
  95. post(OBTAIN_KANBAN_VIEW_DATA, { ...formVal }).then(res => {
  96. res.data.forEach((item: any) => {
  97. item.list = setArrList(item.list)
  98. })
  99. emit('kanbanViewClick', 'multipleChoice', [])
  100. viewList.value = res.data || []
  101. }).finally(() => {
  102. allLoading.kanbanViewLoading = false
  103. })
  104. }
  105. function setArrList(value: any) {
  106. const val = Array.isArray(value) ? value : []
  107. return val.map((item: any) => {
  108. return {
  109. ...item,
  110. expectedTransactionDate: item.expectedTransactionDate ? formatDate(new Date(item.expectedTransactionDate)) : '',
  111. multipleChoice: false,
  112. loadData: false
  113. }
  114. })
  115. }
  116. defineExpose({
  117. searchDashboardView,
  118. });
  119. </script>
  120. <template>
  121. <div class="w-full h-full overflow-auto flex pb-3 scroll-bar" v-loading="allLoading.kanbanViewLoading">
  122. <template v-if="viewList.length > 0">
  123. <div class="w-auto h-full" v-for="(item, index) in viewList" :key="index">
  124. <div class="h-full flex flex-col mx-3 w-72">
  125. <div class="w-full flex justify-between px-3 py-3">
  126. <div class="w-9/12">
  127. <div class="w-auto px-4 py-1 text-white rounded-2xl" :style="{ backgroundColor: item.color }">
  128. {{ item.label }}
  129. </div>
  130. </div>
  131. <div class="w-2/12 text-right">{{ item.length }}</div>
  132. </div>
  133. <VueDraggable v-model="item.list" class="flex-1 overflow-y-auto overflow-x-hidden scroll-bar" :animation="150"
  134. group="people" @update="onChange" @add="onChange" :id="item.id">
  135. <div
  136. class="break-words border border-inherit rounded h-72 mx-3 my-3 p-3 shadow-md hover:shadow-xl duration-300 ease-in-out cursor-pointer"
  137. v-for="(subItem, subIndex) in item.list" :key="subIndex" v-loading="subItem.loadData">
  138. <div class="w-full text-left text-lg flex flex-row items-center" :style="{ color: item.color }">
  139. <el-checkbox v-model="subItem.multipleChoice" class="pr-2" size="large"
  140. @change="(val: any) => selectData(val, subItem)" />
  141. <div class="flex-1 truncate" @click.stop="toDetailPath(subItem)" v-ellipsis-tooltip>
  142. {{ subItem.name }}
  143. </div>
  144. <el-dropdown placement="bottom-start">
  145. <el-link :icon="MoreFilled" :underline="false"></el-link>
  146. <template #dropdown>
  147. <el-dropdown-menu>
  148. <el-dropdown-item @click="addTaskItem(subItem)">新建任务</el-dropdown-item>
  149. <el-dropdown-item @click="switchingStages(subItem)">切换阶段</el-dropdown-item>
  150. <el-dropdown-item @click="editItem(subItem)">编辑</el-dropdown-item>
  151. <el-dropdown-item @click="deteleItem(subItem)">删除</el-dropdown-item>
  152. </el-dropdown-menu>
  153. </template>
  154. </el-dropdown>
  155. </div>
  156. <div class="flex items-center mt-4">
  157. <SvgIcon name="kehu" :size="20" class="mr-2" />
  158. {{ subItem.customerName }}
  159. </div>
  160. <div class="flex items-center mt-4">
  161. <SvgIcon name="lianxiren" :size="20" color="#606266" class="mr-2" />
  162. {{ subItem.contactsName }}
  163. </div>
  164. <div class="flex items-center mt-4">
  165. <SvgIcon name="fuzeren" :size="20" color="#606266" class="mr-2" />
  166. {{ subItem.inchargerName }}
  167. </div>
  168. <div class="flex items-center mt-4">
  169. ¥ {{ subItem.amountOfMoney || 0 }}
  170. </div>
  171. <div class="flex items-center mt-4">
  172. {{ subItem.expectedTransactionDate }}
  173. </div>
  174. </div>
  175. </VueDraggable>
  176. </div>
  177. </div>
  178. </template>
  179. <template v-if="viewList.length == 0">
  180. <div class="w-full h-full flex items-center justify-center">
  181. <el-empty description="暂无数据" />
  182. </div>
  183. </template>
  184. <!-- 弹窗 -->
  185. <el-dialog width="700px" v-model="switchingStagesVisable" append-to-body :show-close="false">
  186. <template #header="{ close, titleId, titleClass }">
  187. <div class="flex justify-between items-center border-b pb-3 dialog-header">
  188. <h4 :id="titleId">切换阶段</h4>
  189. <div>
  190. <el-button type="primary" @click="promotionStage()" :loading="allLoading.switchingStagesLoading">保存</el-button>
  191. <el-button @click="switchingStagesVisable = false">取消</el-button>
  192. </div>
  193. </div>
  194. </template>
  195. <div class="h-[80px] flex flex-col pt-5">
  196. <div class="flex flex-row w-full items-center">
  197. <div class="w-[100px] mr-2 text-right">切换阶段:</div>
  198. <div class="flex-1">
  199. <el-select v-model="selectionStage" placeholder="请选择" style="width: 240px">
  200. <el-option v-for="item in viewList" :key="item.id" :label="item.label" :value="item.id" />
  201. </el-select>
  202. </div>
  203. </div>
  204. </div>
  205. </el-dialog>
  206. </div>
  207. </template>
  208. <style lang="scss" scoped></style>