index.vue 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. <!-- 餐饮公司关系管理 -->
  2. <!-- art-full-height 自动计算出页面剩余高度 -->
  3. <!-- art-table-card 一个符合系统样式的 class,同时自动撑满剩余高度 -->
  4. <!-- 更多 useTable 使用示例请移步至 功能示例 下面的 高级表格示例 -->
  5. <template>
  6. <div class="user-page art-full-height">
  7. <!-- 搜索栏 -->
  8. <UserSearch
  9. v-model="searchForm"
  10. @search="handleSearch"
  11. @reset="resetSearchParams"
  12. :selectList="selectList"
  13. :user-list="userList"
  14. labelPosition="left"
  15. ></UserSearch>
  16. <ElCard class="art-table-card" shadow="never">
  17. <!-- 表格头部 -->
  18. <ArtTableHeader v-model:columns="columnChecks" @refresh="refreshData">
  19. <template #left>
  20. <ElButton type="primary" @click="showDialog('add')" v-ripple v-auth="120201">新增餐饮公司关系</ElButton>
  21. </template>
  22. </ArtTableHeader>
  23. <!-- 表格 -->
  24. <ArtTable
  25. :loading="loading"
  26. :data="data"
  27. :columns="columns"
  28. :pagination="pagination"
  29. @selection-change="handleSelectionChange"
  30. @pagination:size-change="handleSizeChange"
  31. @pagination:current-change="handleCurrentChange"
  32. >
  33. <template #follow_list="scope">
  34. <el-row>
  35. <el-col :sm="20">
  36. <FollowProver :follow_list="scope.row.follow_list" />
  37. </el-col>
  38. <el-col :sm="4">
  39. <ElButton size="small" @click="follow(scope.row)" v-ripple v-auth="120301" type="primary">跟进</ElButton>
  40. </el-col>
  41. </el-row>
  42. </template>
  43. </ArtTable>
  44. <!-- 餐饮公司关系弹窗 -->
  45. <UserDialog
  46. v-model:visible="dialogVisible"
  47. :type="dialogType"
  48. :user-data="currentUserData"
  49. :selectList="selectList"
  50. @submit="handleDialogSubmit"
  51. />
  52. <!-- 跟进弹窗 -->
  53. <FollowDialog
  54. v-model:visible="followDialogVisible"
  55. :user-data="currentUserData"
  56. :type="'company'"
  57. :first_id="currentUserData.company_id || 0"
  58. :second_id="currentUserData.id || 0"
  59. :selectList="selectList"
  60. @submit="handleDialogSubmit"
  61. />
  62. </ElCard>
  63. <!-- 跟进详情显示 -->
  64. <el-drawer
  65. v-model="drawer"
  66. direction="rtl"
  67. size="60%"
  68. >
  69. <template #header>
  70. <span style="font-size: 20px; font-weight: bold;">校方联系人</span>
  71. </template>
  72. <ElRow>
  73. <ElCol :sm="8">
  74. <ElRow class="detail">
  75. <el-col>
  76. <label>关系人:</label> <span>{{ currentRow.name }}</span>
  77. </el-col>
  78. <el-col>
  79. <label>手机:</label> <span>{{ currentRow.phone }}</span>
  80. </el-col>
  81. <el-col>
  82. <label>微信:</label> <span>{{ currentRow.weixin }}</span>
  83. </el-col>
  84. <el-col>
  85. <label>职位:</label> <span>{{ currentRow.position }}</span>
  86. </el-col>
  87. <el-col>
  88. <label>餐饮公司:</label> <span>{{ currentRow.company_name }}</span>
  89. </el-col>
  90. <el-col>
  91. <label>备注:</label> <span>{{ currentRow.memo }}</span>
  92. </el-col>
  93. </ElRow>
  94. </ElCol>
  95. <ElCol :sm="16">
  96. <FollowDrawer :first_id="currentRow.company_id" :second_id="currentRow.id" type="company" :uid="drawerUid"/>
  97. </ElCol>
  98. </ElRow>
  99. </el-drawer>
  100. </div>
  101. </template>
  102. <script setup lang="ts">
  103. import ArtButtonTable from '@/components/core/forms/art-button-table/index.vue'
  104. import {ElMessageBox, ElMessage, ElTag, ElImage, ElButton} from 'element-plus'
  105. import { useTable } from '@/composables/useTable'
  106. import UserSearch from './modules/user-search.vue'
  107. import UserDialog from './modules/user-dialog.vue'
  108. import { companyRelationApi } from '@/api/companyRelationApi'
  109. import { useUserStore } from '@/store/modules/user'
  110. import {followApi} from "@/api/followApi";
  111. import {commonApi} from "@/api/commonApi";
  112. import { detectDeviceType } from '@/utils'
  113. import FollowProver from "@/components/custom/FollowProver.vue";
  114. defineOptions({ name: 'companyRelation' })
  115. type CompanyContactItem = Api.Company.contactItem
  116. const { list } = companyRelationApi
  117. // 弹窗相关
  118. const dialogType = ref<Form.DialogType>('add')
  119. const dialogVisible = ref(false)
  120. const followDialogVisible = ref(false)
  121. const currentUserData = ref<Partial<CompanyContactItem>>({})
  122. // 选中行
  123. const selectedRows = ref<CompanyContactItem[]>([])
  124. // 搜索表单
  125. const searchForm = ref({
  126. name: '',
  127. phone: '',
  128. company_id: parseInt(<string>useRoute().query.company_id) || '',
  129. date: useRoute().query.date || '',
  130. last_user_id:0,
  131. last_date:[],
  132. bind_user_id:0,
  133. })
  134. const userList = ref<Api.Common.SelectInfo[]>([])
  135. const selectList = ref<Api.Common.SelectRelationInfo[]>([])
  136. const getSelectList = async () => {
  137. await commonApi.selectList(['company_relation', 'user']).then(res => {
  138. selectList.value = res.company_relation || []
  139. userList.value = res.user || []
  140. })
  141. }
  142. getSelectList()
  143. const {
  144. columns,
  145. columnChecks,
  146. data,
  147. loading,
  148. pagination,
  149. getData,
  150. searchParams,
  151. resetSearchParams,
  152. handleSizeChange,
  153. handleCurrentChange,
  154. refreshData
  155. } = useTable<CompanyContactItem>({
  156. // 核心配置
  157. core: {
  158. apiFn: list,
  159. apiParams: {
  160. current: 1,
  161. size: 20,
  162. ...searchForm.value
  163. },
  164. // 排除 apiParams 中的属性
  165. excludeParams: ['daterange'],
  166. columnsFactory: () => [
  167. {
  168. prop: 'name', label: '关系人', formatter: (row) => {
  169. return h(ElButton, {
  170. type: 'primary',
  171. link: true,
  172. onClick: () => showDrawer(row),
  173. style: {"text-decoration": 'underline'}
  174. }, () => row.name)
  175. }
  176. },
  177. { prop: 'company_name', label: '餐饮公司' },
  178. { prop: 'phone', label: '手机号' },
  179. { prop: 'weixin', label: '微信号' },
  180. { prop: 'position', label: '职位' },
  181. {prop: 'follow_list', label: '跟进记录', useSlot: true, width: 400},
  182. { prop:'last_user_name', label:'最后一次跟进人' },
  183. { prop:'last_date', label:'最后一次跟进时间' },
  184. { prop: 'memo', label: '备注', showOverflowTooltip: true },
  185. {
  186. prop: 'operation',
  187. label: '操作',
  188. width: detectDeviceType() == 'Mobile' ? 60 : 120,
  189. fixed: 'right', // 固定列
  190. formatter: (row) =>
  191. useUserStore().checkAuth(120202) &&
  192. h('div', [
  193. h(ArtButtonTable, {
  194. type: 'edit',
  195. onClick: () => showDialog('edit', row)
  196. }),
  197. useUserStore().checkAuth(120203) &&
  198. h(ArtButtonTable, {
  199. type: 'delete',
  200. onClick: () => deleteUser(row)
  201. })
  202. ])
  203. }
  204. ]
  205. }
  206. })
  207. /**
  208. * 搜索处理
  209. * @param params 参数
  210. */
  211. const handleSearch = (params: Record<string, any>) => {
  212. Object.assign(searchParams, { ...params })
  213. getData()
  214. }
  215. const detaltValue = <Api.Company.contactItem>{
  216. id: 0,
  217. name: '',
  218. company_id: 0, // 餐饮公司ID,
  219. company_name: '',
  220. phone: '',
  221. weixin: '',
  222. position: '',
  223. memo: '',
  224. last_user_id: 0,
  225. last_user_name: '',
  226. last_date: '',
  227. }
  228. const currentRow = ref<Api.Company.contactItem>({...detaltValue})
  229. const drawer = ref(false)
  230. const showDrawer = (row: Api.Company.contactItem): void => {
  231. drawer.value = true;
  232. currentRow.value = row
  233. }
  234. /**
  235. * 显示餐饮公司关系弹窗
  236. */
  237. const showDialog = (type: Form.DialogType, row?: CompanyContactItem): void => {
  238. dialogType.value = type
  239. currentUserData.value = row || {}
  240. nextTick(() => {
  241. dialogVisible.value = true
  242. })
  243. }
  244. /**
  245. * 显示跟进弹窗
  246. */
  247. const follow = (row: CompanyContactItem): void => {
  248. currentUserData.value = row || {}
  249. nextTick(() => {
  250. followDialogVisible.value = true
  251. })
  252. }
  253. /**
  254. * 删除餐饮公司关系
  255. */
  256. const deleteUser = (row: CompanyContactItem): void => {
  257. ElMessageBox.confirm(`确定要注销该餐饮公司关系吗?`, '注销餐饮公司关系', {
  258. confirmButtonText: '确定',
  259. cancelButtonText: '取消',
  260. type: 'error'
  261. }).then(() => {
  262. companyRelationApi.delete({ id: row.id })
  263. // 延迟更新 不然数据可能没更新
  264. setTimeout(() => {
  265. refreshData()
  266. }, 1000)
  267. })
  268. }
  269. const drawerUid = ref(0)
  270. /**
  271. * 处理弹窗提交事件
  272. */
  273. const handleDialogSubmit = async () => {
  274. try {
  275. dialogVisible.value = false
  276. followDialogVisible.value = false
  277. drawerUid.value++
  278. await getSelectList()
  279. // 延迟更新 不然数据可能没更新
  280. setTimeout(() => {
  281. refreshData()
  282. }, 1000)
  283. } catch (error) {
  284. console.error('提交失败:', error)
  285. }
  286. }
  287. /**
  288. * 处理表格行选择变化
  289. */
  290. const handleSelectionChange = (selection: CompanyContactItem[]): void => {
  291. selectedRows.value = selection
  292. }
  293. </script>
  294. <style lang="scss" scoped>
  295. .user-page {
  296. :deep(.user) {
  297. .avatar {
  298. width: 40px;
  299. height: 40px;
  300. margin-left: 0;
  301. border-radius: 6px;
  302. }
  303. > div {
  304. margin-left: 10px;
  305. .user-name {
  306. font-weight: 500;
  307. color: var(--art-text-gray-800);
  308. }
  309. }
  310. }
  311. }
  312. .detail {
  313. //padding-top: 20px;
  314. font-size: 14px;
  315. .el-col {
  316. margin-bottom: 30px;
  317. label {
  318. font-weight: bold;
  319. margin-right: 5px;
  320. }
  321. }
  322. }
  323. </style>