index.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  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. <el-form-item v-for="(item, index) in filterItems" :key="index" :label="item.label">
  9. <el-input v-if="item.type === 'input'" v-model="filterForm[item.key as keyof FilterForm]" clearable
  10. placeholder="请输入"></el-input>
  11. <personnel-search v-model="filterForm[item.key as keyof FilterForm]" :size="''" placeholder="请选择" v-else-if="['ownerId', 'creatorId'].includes(item.key) && item.type != 'input'"></personnel-search>
  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. </el-form>
  17. </div>
  18. <div class="w-full flex p-3 shadow-[0_-3px_5px_0px_rgba(0,0,0,0.2)]">
  19. <El-button class="w-full" @click="resetForm()" :loading="allLoading.formTableLading">重置</El-Button>
  20. <El-button type="primary" class="w-full" :loading="allLoading.formTableLading"
  21. @click="getContactPerson()">搜索</El-Button>
  22. </div>
  23. </div>
  24. </div>
  25. <div class="flex-1 p-5 overflow-auto">
  26. <div class="bg-white w-full h-full p-3 shadow-md rounded-md flex flex-col">
  27. <div class="flex justify-end pb-3">
  28. <!-- 操作按钮 -->
  29. <!-- <el-button v-for="(button, index) in actionButtons" :key="index" type="primary">{{ button.text }}</el-button> -->
  30. <el-button type="primary" v-permission="['contactsAdd']" @click="editContacts(false)">新建联系人</el-button>
  31. <el-button type="primary" v-permission="['contactsDelete']" @click="batchDeteleItem" :disabled="batchTableData.length <= 0">批量删除</el-button>
  32. <el-button type="primary" v-permission="['contactsRecycle']" @click="showVisible('deteleContactsVisible')">回收站</el-button>
  33. <el-button type="primary" v-permission="['contactsImport']" @click="showVisible('importVisible')">导入</el-button>
  34. <el-button type="primary" v-permission="['contactsExport']" @click="exportCustomerTableList()" :loading="allLoading.exoprtLoading">导出</el-button>
  35. </div>
  36. <div class="flex-1 w-full overflow-hidden">
  37. <!-- 表格 -->
  38. <el-table ref="contactsTableRef" :data="formTable" border v-loading="allLoading.formTableLading"
  39. style="width: 100%;height: 100%;" @selection-change="changeBatch">
  40. <el-table-column type="selection" width="55" />
  41. <el-table-column v-for="(column, index) in tableColumns" :key="index" :prop="column.prop"
  42. :label="column.label" :width="column.width">
  43. <template #default="scope">
  44. <template v-if="column.event === 'toDetali'">
  45. <el-button link type="primary" size="large" @click="toDetali(scope.row)">{{ scope.row.name
  46. }}</el-button>
  47. </template>
  48. <template v-if="['creatorName', 'ownerName'].includes(column.prop)">
  49. <TextTranslation translationTypes="userName" :translationValue="scope.row[column.prop]"></TextTranslation>
  50. </template>
  51. <template v-if="column.event === 'getSex'">
  52. {{ getSex(scope.row.sex) }}
  53. </template>
  54. </template>
  55. </el-table-column>
  56. <el-table-column :label="'操作'" :width="'200px'" fixed="right" v-permission="['contactsEdit', 'tasksAdd', 'contactsDelete']">
  57. <template #default="scope">
  58. <el-button link type="primary" size="large" v-permission="['contactsEdit']" @click="editContacts(scope.row)">编辑</el-button>
  59. <el-button link type="primary" size="large" v-permission="['tasksAdd']" @click="newTask(scope.row)">新建任务</el-button>
  60. <el-button link type="danger" size="large" v-permission="['contactsDelete']"
  61. @click="contactsDeteleItem(scope.row.id, scope.row.name)">删除</el-button>
  62. </template>
  63. </el-table-column>
  64. </el-table>
  65. </div>
  66. <div class="flex justify-end pt-3">
  67. <!-- 分页 -->
  68. <el-pagination layout="total, prev, pager, next, sizes" @size-change="handleSizeChange"
  69. @current-change="handleCurrentChange" :total="tableTotal" :hide-on-single-page="true" />
  70. </div>
  71. </div>
  72. </div>
  73. <!-- 弹窗 -->
  74. <el-dialog v-model="allVisible.editContactsVisible" width="1000" :show-close="false" top="10vh">
  75. <template #header="{ close, titleId, titleClass }">
  76. <div class="flex justify-between items-center border-b pb-3 dialog-header">
  77. <h4 :id="titleId">{{ allText.editContactsText }}</h4>
  78. <div>
  79. <el-button type="primary" :loading="allLoading.editContactsSaveLoading" v-if="!contactsTemplateValue.id"
  80. @click="editContactsSave(true)">保存并新建</el-button>
  81. <el-button type="primary" :loading="allLoading.editContactsSaveLoading"
  82. @click="editContactsSave(false)">保存</el-button>
  83. <el-button @click="closeVisible('editContactsVisible')">取消</el-button>
  84. </div>
  85. </div>
  86. </template>
  87. <div class="h-[60vh] overflow-y-auto scroll-bar pt-3">
  88. <div class="ml-4 mr-4">
  89. <GenerateForm ref="contactsTemplateRef" :data="contactsTemplate" :value="contactsTemplateValue"
  90. :key="contactsTemplateRefKey" v-loading="allLoading.contactsTemplateRefLoading" />
  91. </div>
  92. </div>
  93. </el-dialog>
  94. <!-- 回收站 -->
  95. <DeteleTables :visibles="allVisible.deteleContactsVisible" @closeVisible="closeVisible" />
  96. <!-- 导入 -->
  97. <el-dialog v-model="allVisible.importVisible" width="680" :show-close="false" top="10vh">
  98. <template #header="{ close, titleId, titleClass }">
  99. <div class="flex justify-between items-center border-b pb-3 dialog-header">
  100. <h4 :id="titleId">导入联系人</h4>
  101. <div class="flex">
  102. <el-upload class="upload-demo mr-3" :limit="1" :show-file-list="false" accept=".xlsx"
  103. :http-request="importBusiness">
  104. <el-button type="primary" :loading="allLoading.importLoading">导入</el-button>
  105. </el-upload>
  106. <el-button @click="allVisible.importVisible = false">取消</el-button>
  107. </div>
  108. </div>
  109. </template>
  110. <div class="p-8">
  111. <div class="ml-4 mr-4">
  112. <div class="flex items-center">1、点击下载 <el-link type="primary"
  113. @click="downloadTemplate(IMPORTMOD, allText.importText)">{{ allText.importText }}</el-link></div>
  114. <div class="mt-4">2、填写excel文件、联系人、客户名称必填</div>
  115. </div>
  116. </div>
  117. </el-dialog>
  118. <!-- 任务 -->
  119. <TaskModal :visible="allVisible.taskModalVisible" :edit-form="taskModalForm" :save-loading="taskLoading"
  120. @close="closeVisible('taskModalVisible')" @submit="submitForm" :title="'新建任务'"
  121. :disabled-list="['contactsId']" />
  122. </div>
  123. </template>
  124. <script lang="ts" setup>
  125. import { ref, reactive, onMounted, inject } from "vue";
  126. import { getAllListByCode, getFromValue, resetFromValue, getTemplateKey, confirmAction, downloadTemplate, downloadFile, createTaskFromType } from '@/utils/tools'
  127. import { post, get, uploadFile } from "@/utils/request";
  128. import { actionButtons, tableColumns, GETSYSFILED, GETPERSONNEL, GETGENERATEFOEM, MOD, URL_PAGECONTACTS, getSex, URL_ADD, URL_UPLOAD, URL_BATCHDETELE, URL_DETELERECYCLE, IMPORTMOD, URL_IMPORTDATACONTACTS, URL_EXPORTDATACONTACTS } from "./api";
  129. import { useRouter, useRoute } from "vue-router";
  130. import { GenerateForm } from '@zmjs/form-design';
  131. import { URL_FETALL } from "../customer/api";
  132. import { ElTable, UploadRequestOptions } from "element-plus";
  133. import DeteleTables from './component/deteleTables.vue'
  134. import TaskModal from '@/components/TaskModal/index.vue'
  135. import { createTask } from "@/components/TaskModal/taskFunction";
  136. import personnelSearch from '@/components/translationComponent/personnelSearch/personnelSearch.vue';
  137. const router = useRouter()
  138. const globalPopup = inject<GlobalPopup>('globalPopup')
  139. const filterForm = reactive<FilterForm>({ // 筛选条件
  140. contactPerson: "",
  141. customerId: "",
  142. phoneNumber: '',
  143. responsibleId: '',
  144. createId: '',
  145. email: '',
  146. });
  147. const formTablePaging = reactive({ // 分页条件
  148. pageIndex: 1,
  149. pageSize: 10,
  150. })
  151. const tableTotal = ref(0)
  152. const selectData = reactive({ // 下拉数据
  153. Personnel: [] as personnelInterface[],
  154. Customer: [] as any[],
  155. })
  156. const filterItems = ref<FilterItem[]>([
  157. { label: '联系人', key: 'name', type: 'input' },
  158. { label: '客户名称', key: 'customId', type: 'select', options: selectData.Customer },
  159. { label: '电话号码', key: 'phone', type: 'input' },
  160. { label: '邮箱', key: 'email', type: 'input' },
  161. { label: '负责人', key: 'ownerId', type: 'select', options: selectData.Personnel },
  162. { label: '创建人', key: 'creatorId', type: 'select', options: selectData.Personnel },
  163. ])
  164. const contactsTemplate = ref({
  165. list: [],
  166. config: {}
  167. })
  168. const contactsTemplateValue = ref<any>({})
  169. const contactsTemplateRefKey = ref(1)
  170. const contactsTemplateRef = ref<typeof GenerateForm>()
  171. const contactsTableRef = ref<InstanceType<typeof ElTable>>()
  172. const formTable = ref([]) // 表格数据
  173. const batchTableData = ref([])
  174. const taskModalForm = ref({})
  175. const taskLoading = ref<saveLoadingType>('1')
  176. const allLoading = reactive({ // 按钮加载 Loading
  177. formTableLading: false,
  178. editContactsSaveLoading: false,
  179. contactsTemplateRefLoading: false,
  180. importLoading: false,
  181. exoprtLoading: false
  182. })
  183. const allVisible = reactive({
  184. editContactsVisible: false,
  185. taskModalVisible: false,
  186. deteleContactsVisible: false,
  187. importVisible: false
  188. })
  189. const allText = reactive({
  190. editContactsText: '新建联系人',
  191. importText: '联系人导入模板.xlsx',
  192. exportText: '联系人导出.xlsx'
  193. })
  194. // 方法
  195. function submitForm(submitData: any, isClose: boolean) {
  196. taskLoading.value = '2'
  197. createTask(submitData, isClose).then((res) => {
  198. const { saveLoading, isClose } = res
  199. taskLoading.value = saveLoading
  200. allVisible.taskModalVisible = isClose
  201. globalPopup?.showSuccess('新增成功')
  202. }).catch((err) => {
  203. const { saveLoading, isClose, message } = err
  204. taskLoading.value = saveLoading
  205. allVisible.taskModalVisible = isClose
  206. globalPopup?.showError(message)
  207. })
  208. }
  209. function newTask(item: any) {
  210. const { id } = item
  211. taskModalForm.value = { ...createTaskFromType(0), contactsId: id, }
  212. showVisible('taskModalVisible')
  213. }
  214. function exportCustomerTableList() {
  215. allLoading.exoprtLoading = true
  216. let valueForm = getFromValue(filterForm)
  217. post(URL_EXPORTDATACONTACTS, { ...valueForm }).then((res) => {
  218. downloadFile(res.data, allText.exportText)
  219. }).finally(() => {
  220. allLoading.exoprtLoading = false
  221. })
  222. }
  223. async function importBusiness(param: UploadRequestOptions) {
  224. allLoading.importLoading = true
  225. const formData = new FormData();
  226. formData.append('multipartFile', param.file)
  227. const res = await uploadFile(URL_IMPORTDATACONTACTS, formData).finally(() => {
  228. allLoading.importLoading = false
  229. })
  230. if (res.code == 'ok') {
  231. globalPopup?.showSuccess('导入成功' || '')
  232. getContactPerson()
  233. return
  234. }
  235. globalPopup?.showError(res.msg || '')
  236. }
  237. function batchDeteleItem() {
  238. const value = batchTableData.value.map((item: any) => item.id).join(',')
  239. const label = batchTableData.value.map((item: any) => item.customName).join(',')
  240. contactsDeteleItem(value, label, true)
  241. }
  242. function contactsDeteleItem(value: string | number, label: string, batch: boolean = false) {
  243. confirmAction(`确定${batch ? '批量' : ''}删除【${label}】联系人吗?`).then(() => {
  244. // let url = batch ? URL_BATCHDETELE : URL_DETELERECYCLE
  245. let url = URL_DETELERECYCLE
  246. post(url, { ids: value }).then(res => {
  247. if (res.code != 'ok') {
  248. globalPopup?.showError(res.msg)
  249. return
  250. }
  251. globalPopup?.showSuccess('删除成功')
  252. changeBatch(false)
  253. getContactPerson()
  254. }).catch((err) => {
  255. globalPopup?.showError(err.msg)
  256. })
  257. })
  258. }
  259. function changeBatch(flag: boolean = true) {
  260. if (flag) {
  261. batchTableData.value = contactsTableRef.value && contactsTableRef.value.getSelectionRows()
  262. } else {
  263. batchTableData.value = []
  264. contactsTableRef.value && contactsTableRef.value.clearSelection()
  265. }
  266. }
  267. function editContactsSave(flag: boolean) {
  268. contactsTemplateRef.value?.getData().then((res: any) => {
  269. allLoading.editContactsSaveLoading = true
  270. let url = allText.editContactsText == '新建联系人' ? URL_ADD : URL_UPLOAD
  271. post(url, { ...contactsTemplateValue.value, ...res }).then((_res) => {
  272. allVisible.editContactsVisible = flag
  273. globalPopup?.showSuccess('保存成功')
  274. if (flag) {
  275. contactsTemplateRef.value?.reset()
  276. allText.editContactsText = '新建联系人'
  277. }
  278. getContactPerson()
  279. }).finally(() => {
  280. allLoading.editContactsSaveLoading = false
  281. })
  282. }).catch((_err: any) => {
  283. console.log(_err)
  284. globalPopup?.showError('请填写完整')
  285. })
  286. }
  287. function editContacts(row: any) { // row 有数据代表编辑
  288. showVisible('editContactsVisible')
  289. if (row) {
  290. const templateKey = getTemplateKey(contactsTemplate.value.list)
  291. const formVal: templateKey = { id: row.id }
  292. for (let i in templateKey) {
  293. if(row[templateKey[i]]) {
  294. formVal[templateKey[i]] = templateKey[i] == 'sex' ? row[templateKey[i]] + '' : row[templateKey[i]]
  295. }
  296. }
  297. setTemplateVal(formVal)
  298. allText.editContactsText = '编辑联系人'
  299. } else {
  300. setTemplateVal()
  301. allText.editContactsText = '新建联系人'
  302. }
  303. }
  304. function setTemplateVal(val: any = {}) {
  305. console.log(val)
  306. contactsTemplateValue.value = val
  307. allLoading.contactsTemplateRefLoading = true
  308. setTimeout(() => {
  309. contactsTemplateRefKey.value++
  310. allLoading.contactsTemplateRefLoading = false
  311. }, 1000);
  312. }
  313. function toDetali(row: any) {
  314. router.push({
  315. path: `${MOD}/detail`,
  316. query: { id: row.id }
  317. })
  318. }
  319. function getContactPerson() {
  320. allLoading.formTableLading = true
  321. const formVal = getFromValue(filterForm)
  322. post(URL_PAGECONTACTS, { ...formVal, ...formTablePaging }).then((res) => {
  323. const { records, total } = res.data
  324. formTable.value = records
  325. tableTotal.value = total
  326. }).finally(() => {
  327. allLoading.formTableLading = false
  328. })
  329. }
  330. function resetForm() {
  331. let newResetForm = resetFromValue(filterForm)
  332. Object.assign(filterForm, newResetForm)
  333. getContactPerson()
  334. }
  335. async function getSystemField() {
  336. const { data } = await post(GETPERSONNEL, {})
  337. selectData.Personnel = data.map((item: any) => {
  338. const { id, name, phone, jobNumber } = item
  339. return {
  340. id, name, phone, jobNumber
  341. }
  342. })
  343. const res = await get(URL_FETALL, {})
  344. selectData.Customer = (res.data || []).map((item: any) => {
  345. const { id, customName } = item
  346. return {
  347. id,
  348. name: customName
  349. }
  350. })
  351. const datas = await get(GETGENERATEFOEM)
  352. contactsTemplate.value = JSON.parse(datas.data[0].config)
  353. setFilterItems()
  354. }
  355. function handleSizeChange(val: number) {
  356. formTablePaging.pageIndex = 1
  357. formTablePaging.pageSize = val
  358. getContactPerson()
  359. }
  360. function handleCurrentChange(val: number) {
  361. formTablePaging.pageIndex = val
  362. getContactPerson()
  363. }
  364. function showVisible(type: keyof typeof allVisible) {
  365. allVisible[type] = true
  366. }
  367. function closeVisible(type: keyof typeof allVisible) {
  368. allVisible[type] = false
  369. }
  370. function setFilterItems() {
  371. console.log(selectData)
  372. filterItems.value = [
  373. { label: '联系人', key: 'name', type: 'input' },
  374. { label: '客户名称', key: 'customId', type: 'select', options: selectData.Customer },
  375. { label: '电话号码', key: 'phone', type: 'input' },
  376. { label: '邮箱', key: 'email', type: 'input' },
  377. { label: '负责人', key: 'ownerId', type: 'select', options: selectData.Personnel },
  378. { label: '创建人', key: 'creatorId', type: 'select', options: selectData.Personnel },
  379. ]
  380. }
  381. onMounted(() => {
  382. getSystemField()
  383. getContactPerson()
  384. })
  385. </script>
  386. <style lang="scss" scoped></style>