index.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  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 v-model="searchForm" @search="handleSearch" @reset="resetSearchParams"></UserSearch>
  9. <!-- 跟进弹窗 -->
  10. <FollowDialog
  11. v-model:visible="followDialogVisible"
  12. :user-data="currentRow"
  13. :type="'school'"
  14. :first_id="currentRow.id || 0"
  15. :second_id="0"
  16. :selectList="selectList"
  17. @submit="handleDialogSubmit"
  18. />
  19. <ElCard class="art-table-card" shadow="never">
  20. <!-- 表格头部 -->
  21. <ArtTableHeader v-model:columns="columnChecks" @refresh="refreshData">
  22. <template #left>
  23. <ElButton type="primary" @click="edit()" v-ripple v-auth="120101">新增校区</ElButton>
  24. </template>
  25. </ArtTableHeader>
  26. <!-- 表格 -->
  27. <ArtTable
  28. :loading="loading"
  29. :data="data"
  30. :columns="columns"
  31. :pagination="pagination"
  32. @pagination:size-change="handleSizeChange"
  33. @pagination:current-change="handleCurrentChange"
  34. >
  35. <template #follow_list="scope">
  36. <el-row>
  37. <el-col :sm="20">
  38. <FollowProver :follow_list="scope.row.follow_list" />
  39. </el-col>
  40. <el-col :sm="4">
  41. <ElButton size="small" @click="follow(scope.row)" v-ripple v-auth="120301" type="primary">跟进</ElButton>
  42. </el-col>
  43. </el-row>
  44. </template>
  45. </ArtTable>
  46. <!-- 用户弹窗 -->
  47. <!-- <UserDialog-->
  48. <!-- v-model:visible="dialogVisible"-->
  49. <!-- :type="dialogType"-->
  50. <!-- :user-data="currentUserData"-->
  51. <!-- :role-list="roleList"-->
  52. <!-- @submit="handleDialogSubmit"-->
  53. <!-- />-->
  54. </ElCard>
  55. <el-drawer
  56. v-model="drawer"
  57. direction="rtl"
  58. size="70%"
  59. >
  60. <template #header>
  61. <span style="font-size: 20px; font-weight: bold;">{{ currentRow.name }}</span>
  62. </template>
  63. <ElRow>
  64. <el-col :sm="10">
  65. <ElRow class="detail">
  66. <el-col :sm="12">
  67. <label>在校人数:</label> <span>{{ currentRow.person_num }}</span>
  68. </el-col>
  69. <el-col :sm="12">
  70. <label>地区:</label> <span>{{
  71. [currentRow.province, currentRow.city, currentRow.area].join(' / ')
  72. }}</span>
  73. </el-col>
  74. <el-col :span=24>
  75. <label>详细地址:</label> <span>{{ currentRow.address }}</span>
  76. </el-col>
  77. <el-col :sm="12">
  78. <label>负责人:</label> <span>{{ currentRow.bind_user_id }}</span>
  79. </el-col>
  80. <el-col :sm="12">
  81. <label>是否有饿了么校内站:</label> <span>{{ currentRow.is_eleme_in_school ? '有' : '无' }}</span>
  82. </el-col>
  83. <el-col :sm="12">
  84. <label>是否有饿了么校外站:</label> <span>{{ currentRow.is_eleme_out_school ? '有' : '无' }}</span>
  85. </el-col>
  86. <el-col :sm="12">
  87. <label>是否有美团校内站:</label> <span>{{ currentRow.is_meituan_in_school ? '有' : '无' }}</span>
  88. </el-col>
  89. <el-col :sm="12">
  90. <label>是否有美团校外站:</label> <span>{{ currentRow.is_meituan_out_school ? '有' : '无' }}</span>
  91. </el-col>
  92. <el-col :sm="12">
  93. <label>是否能上楼:</label> <span>{{ currentRow.can_go_upstairs ? '不能' : '能' }}</span>
  94. </el-col>
  95. <el-col :sm="12">
  96. <label>是否合作:</label> <span>{{ currentRow.is_cooperate ? '已合作' : '未合作' }}</span>
  97. </el-col>
  98. <el-col :sm="12">
  99. <label>是否允许骑电动车:</label> <span>{{ currentRow.can_ride ? '不能' : '能' }}</span>
  100. </el-col>
  101. <el-col :span=24>
  102. <label>宿舍分布情况:</label> <span>{{ currentRow.dormitory_distribution }}</span>
  103. </el-col>
  104. <el-col :span=24>
  105. <label>校门口取餐点离宿舍情况:</label> <span>{{ currentRow.qucan_station_distribution }}</span>
  106. </el-col>
  107. <el-col :span=24>
  108. <label>校外商圈情况:</label> <span>{{ currentRow.out_business_description }}</span>
  109. </el-col>
  110. <el-col :span=24>
  111. <label>负责人:</label> <span>{{ currentRow.bind_user_name }}</span>
  112. </el-col>
  113. <el-col v-auth="120200">
  114. <label>校方关系人:</label>
  115. <el-table border :data="schoolInfo?.canteens || []" style="width: 100%; margin-top: 10px">
  116. <el-table-column prop="name" label="食堂" />
  117. <el-table-column prop="username" label="食堂经理" />
  118. <el-table-column prop="phone" label="手机号" />
  119. <el-table-column prop="weixin" label="微信号" />
  120. </el-table>
  121. </el-col>
  122. <el-col v-auth="130100">
  123. <label>食堂信息:</label>
  124. <el-table border :data="schoolInfo?.relations || []" style="width: 100%; margin-top: 10px">
  125. <el-table-column prop="name" label="姓名" />
  126. <el-table-column prop="position" label="职位" />
  127. <el-table-column prop="phone" label="手机号" />
  128. <el-table-column prop="weixin" label="微信号" />
  129. </el-table>
  130. </el-col>
  131. </ElRow>
  132. </el-col>
  133. <el-col :sm="14">
  134. <FollowDrawer :first_id="currentRow.id" :second_id="0" type="school" :uid="drawerUid"/>
  135. </el-col>
  136. </ElRow>
  137. </el-drawer>
  138. </div>
  139. </template>
  140. <script setup lang="ts">
  141. import ArtButtonTable from '@/components/core/forms/art-button-table/index.vue'
  142. import {ElMessageBox, ElMessage, ElTag, ElImage, ElButton, ElInput} from 'element-plus'
  143. import {useTable} from '@/composables/useTable'
  144. import {schoolApi} from '@/api/schoolApi'
  145. import UserSearch from './user-search.vue'
  146. import {useUserStore} from '@/store/modules/user'
  147. import EmojiText from '@utils/ui/emojo'
  148. import {router} from '@/router'
  149. import {RoutesAlias} from '@/router/routesAlias'
  150. import {followApi} from "@/api/followApi";
  151. import {schoolRelationApi} from "@/api/schoolRelationApi";
  152. import {commonApi} from "@/api/commonApi";
  153. import { detectDeviceType } from '@/utils'
  154. import FollowProver from "@/components/custom/FollowProver.vue";
  155. defineOptions({name: 'User'})
  156. const {list} = schoolApi
  157. const drawer = ref(false)
  158. const followDialogVisible = ref(false)
  159. // 搜索表单
  160. const searchForm = ref({
  161. name: '',
  162. is_cooperate: -1,
  163. address: []
  164. })
  165. const defaultValue = <Api.School.SchoolListItem>{
  166. id: 0,
  167. name: '',
  168. province: '',
  169. city: '',
  170. area: '',
  171. address: '',
  172. person_num: '',
  173. bind_user_id: 0,
  174. is_eleme_in_school: 0,
  175. is_eleme_out_school: 0,
  176. is_meituan_in_school: 0,
  177. is_meituan_out_school: 0,
  178. can_go_upstairs: 0,
  179. is_cooperate: 0,
  180. can_ride: 0,
  181. dormitory_distribution: '',
  182. qucan_station_distribution: '',
  183. out_business_description: '',
  184. memo: ''
  185. }
  186. const currentRow = ref<Api.School.SchoolListItem>({...defaultValue})
  187. const selectList = ref<Api.Common.SelectRelationInfo[]>([])
  188. const getSelectList = async () => {
  189. await commonApi.selectList(['school_relation']).then(res => {
  190. selectList.value = res.school_relation || []
  191. })
  192. }
  193. getSelectList()
  194. const drawerUid = ref(0)
  195. /**
  196. * 处理弹窗提交事件
  197. */
  198. const handleDialogSubmit = async () => {
  199. followDialogVisible.value = false
  200. drawerUid.value++
  201. getData()
  202. }
  203. const {
  204. columns,
  205. columnChecks,
  206. data,
  207. loading,
  208. pagination,
  209. getData,
  210. searchParams,
  211. resetSearchParams,
  212. handleSizeChange,
  213. handleCurrentChange,
  214. refreshData
  215. } = useTable<Api.School.SchoolListItem>({
  216. // 核心配置
  217. core: {
  218. apiFn: list,
  219. apiParams: {
  220. current: 1,
  221. size: 20,
  222. ...searchForm.value
  223. },
  224. // 排除 apiParams 中的属性
  225. excludeParams: [],
  226. columnsFactory: () => [
  227. {
  228. prop: 'name', label: '学校(校区)', showOverflowTooltip: true, formatter: (row) => {
  229. return h(ElButton, {
  230. type: 'primary',
  231. link: true,
  232. onClick: () => showDrawer(row),
  233. style: {"text-decoration": 'underline'}
  234. }, () => row.name)
  235. }
  236. },
  237. {prop: 'area', label: '地区', formatter: (row) => row.province + row.city + row.area},
  238. {prop: 'address', label: '详细地址'},
  239. {
  240. prop: 'canteen',
  241. label: '关联食堂',
  242. // sortable: true,
  243. // checked: false, // 隐藏列
  244. formatter: (row) => {
  245. return h(ElButton, {type: 'primary', size: 'small', onClick: () => showCanteen(row)}, () => '查看')
  246. }
  247. },
  248. {
  249. prop: 'concat',
  250. label: '关系人',
  251. formatter: (row) => {
  252. return h(ElButton, {type: 'primary', size: 'small', onClick: () => showContact(row)}, () => '查看')
  253. }
  254. },
  255. {
  256. prop: 'is_cooperate',
  257. label: '合作状态',
  258. formatter: (row) => {
  259. return h(ElTag, {type: row.is_cooperate ? 'success' : 'danger'}, () =>
  260. row.is_cooperate ? '已合作' : '未合作'
  261. )
  262. }
  263. },
  264. {prop: 'person_num', label: '在校人数'},
  265. // {prop: 'is_eleme_in_school', label: '是否有饿了么校内站', useSlot: true},
  266. {prop: 'is_eleme_in_school', label: '是否有饿了么校内站', formatter: (row) => {
  267. return h(ElTag, {type: row.is_eleme_out_school ? 'success' : 'danger'}, () =>
  268. row.is_eleme_out_school ? '有' : '无'
  269. )
  270. }},
  271. {
  272. prop: 'is_eleme_out_school',
  273. label: '是否有饿了么校外站',
  274. formatter: (row) => {
  275. return h(ElTag, {type: row.is_eleme_out_school ? 'success' : 'danger'}, () =>
  276. row.is_eleme_out_school ? '有' : '无'
  277. )
  278. }
  279. },
  280. {
  281. prop: 'is_meituan_in_school',
  282. label: '是否有美团校内站',
  283. formatter: (row) => {
  284. return h(ElTag, {type: row.is_meituan_in_school ? 'success' : 'danger'}, () =>
  285. row.is_meituan_in_school ? '有' : '无'
  286. )
  287. }
  288. },
  289. {
  290. prop: 'is_meituan_out_school',
  291. label: '是否有美团校外站',
  292. formatter: (row) => {
  293. return h(ElTag, {type: row.is_meituan_out_school ? 'success' : 'danger'}, () =>
  294. row.is_meituan_out_school ? '有' : '无'
  295. )
  296. }
  297. },
  298. {
  299. prop: 'can_go_upstairs',
  300. label: '是否能上楼',
  301. formatter: (row) => {
  302. return h(ElTag, {type: row.can_go_upstairs ? 'success' : 'danger'}, () =>
  303. row.can_go_upstairs ? '能' : '不能'
  304. )
  305. }
  306. },
  307. {
  308. prop: 'can_ride',
  309. label: '是否允许骑电动车',
  310. formatter: (row) => {
  311. return h(ElTag, {type: row.can_ride ? 'success' : 'danger'}, () =>
  312. row.can_ride ? '能' : '不能'
  313. )
  314. }
  315. },
  316. {prop: 'follow_list', label: '跟进记录', useSlot: true, width: 400},
  317. { prop: 'dormitory_distribution', label: '宿舍分布情况', showOverflowTooltip: true },
  318. { prop: 'qucan_station_distribution', label: '校门口取餐点离宿舍情况', showOverflowTooltip: true },
  319. {prop: 'out_business_description', label: '校外商圈情况', showOverflowTooltip: true},
  320. {prop: 'memo', label: '备注', showOverflowTooltip: true},
  321. {
  322. prop: 'operation',
  323. label: '操作',
  324. width: detectDeviceType() == 'Mobile' ? 60 : 120,
  325. fixed: 'right', // 固定列
  326. formatter: (row) =>
  327. h('div', [
  328. h(ArtButtonTable, {
  329. type: 'view',
  330. onClick: () => view(row.id)
  331. }),
  332. useUserStore().checkAuth(110202) && h(ArtButtonTable, {
  333. type: 'edit',
  334. onClick: () => edit(row.id)
  335. }),
  336. ])
  337. }
  338. ]
  339. }
  340. })
  341. const doUpdateAttr = (scope: any) => {
  342. if (!scope.row.id) {
  343. return
  344. }
  345. schoolApi
  346. .updateAttr({id: scope.row.id, attr: scope.prop, value: scope.row[scope.prop]})
  347. .then(() => {
  348. ElMessage.success(`${EmojiText[200]} 修改成功`)
  349. })
  350. }
  351. /**
  352. * 搜索处理
  353. * @param params 参数
  354. */
  355. const handleSearch = (params: Record<string, any>) => {
  356. Object.assign(searchParams, {...params})
  357. getData()
  358. }
  359. /**
  360. * 编辑
  361. */
  362. const edit = (id?: number): void => {
  363. router.push({
  364. path: RoutesAlias.SchoolEdit,
  365. query: {
  366. id: id
  367. }
  368. })
  369. }
  370. /**
  371. * 显示跟进弹窗
  372. */
  373. const follow = (row: Api.School.SchoolListItem): void => {
  374. currentRow.value = row || {}
  375. nextTick(() => {
  376. followDialogVisible.value = true
  377. })
  378. }
  379. /**
  380. * 查看
  381. */
  382. const view = (id: number): void => {
  383. router.push({
  384. path: RoutesAlias.SchoolInfo,
  385. query: {
  386. id: id
  387. }
  388. })
  389. }
  390. const schoolInfo = ref<Api.School.SchoolInfo>()
  391. const showDrawer = (row: Api.School.SchoolListItem): void => {
  392. drawer.value = true;
  393. currentRow.value = row
  394. schoolApi.info(row.id).then((res) => {
  395. schoolInfo.value = res
  396. })
  397. }
  398. const showContact = (row: Api.School.SchoolListItem):void => {
  399. router.push({
  400. path: RoutesAlias.SchoolRelation,
  401. query: {
  402. school_id: row.id
  403. }
  404. })
  405. }
  406. const showCanteen = (row: Api.School.SchoolListItem):void => {
  407. router.push({
  408. path: RoutesAlias.CanteenList,
  409. query: {
  410. school_id: row.id
  411. }
  412. })
  413. }
  414. /**
  415. * 删除
  416. */
  417. const deleteUser = (id: number): void => {
  418. ElMessageBox.confirm(`确定要删除该学校吗?`, '删除学校', {
  419. confirmButtonText: '确定',
  420. cancelButtonText: '取消',
  421. type: 'error'
  422. }).then(() => {
  423. schoolApi.delete({id: id}).then(() => {
  424. ElMessage.success(`${EmojiText[200]} 删除成功`)
  425. setTimeout(() => {
  426. getData()
  427. }, 1000)
  428. })
  429. })
  430. }
  431. </script>
  432. <style lang="scss" scoped>
  433. .el-button::after {
  434. content: none !important;
  435. }
  436. .user-page {
  437. :deep(.user) {
  438. .avatar {
  439. width: 40px;
  440. height: 40px;
  441. margin-left: 0;
  442. border-radius: 6px;
  443. }
  444. > div {
  445. margin-left: 10px;
  446. .user-name {
  447. font-weight: 500;
  448. color: var(--art-text-gray-800);
  449. }
  450. }
  451. }
  452. }
  453. .detail {
  454. //padding-top: 20px;
  455. font-size: 14px;
  456. padding-right: 5px;
  457. .el-col {
  458. margin-bottom: 30px;
  459. label {
  460. font-weight: bold;
  461. margin-right: 5px;
  462. }
  463. }
  464. }
  465. </style>