kanbanView.vue 8.3 KB

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