index.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. <!--
  2. * @Author: wanglj 471442253@qq.com
  3. * @Date: 2023-02-13 13:51:45
  4. * @LastEditors: wanglj
  5. * @LastEditTime: 2023-02-14 18:05:38
  6. * @Description: file content
  7. * @FilePath: \opms_frontend\src\views\base\regionAuth\index.vue
  8. -->
  9. <template>
  10. <div class="region-auth-container">
  11. <vab-query-form>
  12. <el-form ref="queryForm">
  13. <vab-query-form-left-panel>
  14. <el-form-item>
  15. <el-input v-model="queryForm.keyWords" placeholder="姓名" />
  16. </el-form-item>
  17. <el-form-item>
  18. <el-button icon="el-icon-search" type="primary" @click="handleSearch">查询</el-button>
  19. <el-button icon="el-icon-refresh-right" @click="reset">重置</el-button>
  20. </el-form-item>
  21. </vab-query-form-left-panel>
  22. <vab-query-form-right-panel>
  23. <el-button
  24. v-permissions="['base:regionAuth:grant']"
  25. icon="el-icon-circle-check"
  26. type="primary"
  27. @click="handleAuth">
  28. 授权
  29. </el-button>
  30. <table-tool :check-list.sync="checkList" :columns="columns" />
  31. </vab-query-form-right-panel>
  32. </el-form>
  33. </vab-query-form>
  34. <el-row :gutter="10">
  35. <el-col :span="12">
  36. <el-table
  37. v-loading="listLoading"
  38. border
  39. :data="list"
  40. :height="$baseTableHeight(1)"
  41. style="width: 100%"
  42. @row-click="handleRowClick">
  43. <el-table-column
  44. v-for="(item, index) in finallyColumns"
  45. :key="index"
  46. align="center"
  47. :label="item.label"
  48. :prop="item.prop"
  49. show-overflow-tooltip
  50. :sortable="item.sortable"
  51. :width="item.width">
  52. <template #default="{ row }">
  53. <span>{{ row[item.prop] }}</span>
  54. </template>
  55. </el-table-column>
  56. <el-table-column align="center" label="选择" width="80">
  57. <template #default="{ row }">
  58. <i v-if="form.userId == row.userId" class="el-icon-check"></i>
  59. </template>
  60. </el-table-column>
  61. <template #empty>
  62. <el-image class="vab-data-empty" :src="require('@/assets/empty_images/data_empty.png')" />
  63. </template>
  64. </el-table>
  65. <el-pagination
  66. background
  67. :current-page="queryForm.pageNum"
  68. :layout="layout"
  69. :page-size="queryForm.pageSize"
  70. :total="total"
  71. @current-change="handleCurrentChange"
  72. @size-change="handleSizeChange" />
  73. </el-col>
  74. <el-col :span="12">
  75. <el-table v-loading="listLoading" border :data="regionList" :height="$baseTableHeight(0)" style="width: 100%">
  76. <el-table-column align="center" label="销售区域" prop="saleRegionName" />
  77. <el-table-column align="center" label="省" prop="provinceName" />
  78. <el-table-column align="center" label="市" prop="cityName" />
  79. </el-table>
  80. </el-col>
  81. </el-row>
  82. <el-dialog title="授权区域" top="5vh" :visible.sync="visible" width="80%" @close="handleClose">
  83. <div v-loading="dialogLoading" class="side-layout">
  84. <div class="tree-side">
  85. <el-tree
  86. ref="tree"
  87. class="filter-tree"
  88. :data="deptOptions"
  89. default-expand-all
  90. :default-expanded-keys="[1]"
  91. :filter-node-method="filterNode"
  92. highlight-current
  93. node-key="id"
  94. :props="defaultProps"
  95. @node-click="handleNodeClick">
  96. <span slot-scope="{ node }" class="custom-tree-node">
  97. <span>{{ node.label }}</span>
  98. <span><i class="el-icon-more"></i></span>
  99. </span>
  100. </el-tree>
  101. </div>
  102. <div class="tree-table">
  103. <el-checkbox v-model="checkAll" :indeterminate="isIndeterminate" @change="handleCheckAllChange">
  104. 全选
  105. </el-checkbox>
  106. <div v-for="item in options" :key="item.custProvinceId" class="province" :label="item.custProvince">
  107. <h3>{{ item.custProvince }}</h3>
  108. <div class="check-container">
  109. <el-checkbox
  110. v-model="item.checkAll"
  111. :indeterminate="item.isIndeterminate"
  112. @change="(val) => handleSndCheckAllChange(val, item)">
  113. 全选
  114. </el-checkbox>
  115. <el-checkbox-group v-model="form.checkList" @change="(val) => handleChange(val, item)">
  116. <el-checkbox v-for="city in item.children" :key="city.id" :label="city.custCityId">
  117. {{ city.custCity }}
  118. <span v-if="city.allocator.length">({{ city.allocator.join() }})</span>
  119. </el-checkbox>
  120. </el-checkbox-group>
  121. </div>
  122. </div>
  123. </div>
  124. </div>
  125. <template #footer>
  126. <el-button @click="visible = false">取 消</el-button>
  127. </template>
  128. </el-dialog>
  129. </div>
  130. </template>
  131. <script>
  132. import to from 'await-to-js'
  133. import regionApi from '@/api/base/region'
  134. import regAuthApi from '@/api/base/regionAuth'
  135. import TableTool from '@/components/table/TableTool'
  136. export default {
  137. name: 'RegionAuth',
  138. components: {
  139. TableTool,
  140. },
  141. data() {
  142. return {
  143. queryForm: {
  144. pageNum: 1,
  145. pageSize: 10,
  146. keyWords: '',
  147. deptId: 1001,
  148. },
  149. layout: 'total, sizes, prev, pager, next, jumper',
  150. listLoading: false,
  151. total: 0,
  152. list: [],
  153. regionList: [],
  154. checkList: [],
  155. columns: [
  156. {
  157. label: '姓名',
  158. width: '120px',
  159. prop: 'userName',
  160. sortable: false,
  161. disableCheck: true,
  162. },
  163. {
  164. label: '岗位',
  165. width: 'auto',
  166. prop: 'userPost',
  167. sortable: false,
  168. disableCheck: false,
  169. },
  170. {
  171. label: '电话',
  172. width: 'auto',
  173. prop: 'userPhone',
  174. sortable: false,
  175. disableCheck: false,
  176. },
  177. ],
  178. checkAll: false,
  179. isIndeterminate: false,
  180. form: {
  181. userId: 0,
  182. checkList: [],
  183. },
  184. regionId: 0,
  185. saleRegionName: '',
  186. visible: false,
  187. deptOptions: [],
  188. defaultProps: {
  189. id: 'id',
  190. regionCode: 'regionCode',
  191. label: 'regionDesc',
  192. },
  193. options: [], //区域选项
  194. flatOptions: [], //扁平化区域
  195. dialogLoading: false,
  196. allocated: [], //已被分配的城市
  197. }
  198. },
  199. computed: {
  200. finallyColumns() {
  201. return this.columns.filter((item) => this.checkList.includes(item.label))
  202. },
  203. },
  204. mounted() {
  205. this.fetchData()
  206. },
  207. methods: {
  208. async fetchData() {
  209. const [err, res] = await to(regAuthApi.getList(this.queryForm))
  210. if (err) return
  211. this.list = res.data.list || []
  212. this.total = res.data.total
  213. this.form = {
  214. userId: 0,
  215. checkList: [],
  216. }
  217. this.regionList = []
  218. },
  219. handleRowClick(row) {
  220. this.form.userId = row.userId
  221. this.form.userName = row.userName
  222. this.form.userPost = row.userPost
  223. this.form.userPhone = row.userPhone
  224. this.getSndList()
  225. },
  226. async getSndList() {
  227. const [err, res] = await to(
  228. regAuthApi.getDetail({
  229. userId: this.form.userId,
  230. })
  231. )
  232. if (err) return
  233. this.regionList = res.data.list || []
  234. },
  235. handleSearch() {
  236. this.queryForm.pageNum = 1
  237. this.fetchData()
  238. },
  239. reset() {
  240. this.resetForm('queryForm')
  241. this.fetchData()
  242. },
  243. handleSizeChange(val) {
  244. this.queryForm.pageSize = val
  245. this.fetchData()
  246. },
  247. handleCurrentChange(val) {
  248. this.queryForm.pageNum = val
  249. this.fetchData()
  250. },
  251. async handleAuth() {
  252. if (!this.form.userId) return this.$message.warning('请选择用户')
  253. this.visible = true
  254. this.getRegionList()
  255. },
  256. async getRegionList() {
  257. const { data: data } = await regionApi.getRegionList()
  258. this.deptOptions = data.list
  259. this.regionId = data.list[0].id
  260. this.saleRegionName = data.list[0].regionDesc
  261. //默认第一选中
  262. this.$nextTick(() => {
  263. this.$refs.tree.setCurrentKey(this.regionId)
  264. this.handleNodeClick({ id: this.regionId, regionDesc: this.saleRegionName })
  265. })
  266. },
  267. async handleDetail() {
  268. this.form.checkList = []
  269. this.allocated = []
  270. const [err, res] = await to(
  271. regAuthApi.getDetail({
  272. // userId: this.form.userId,
  273. saleRegionId: this.regionId,
  274. })
  275. )
  276. if (err) return
  277. const list = res.data.list || []
  278. for (const item of list) {
  279. if (item.userId == this.form.userId) this.form.checkList.push(item.cityId)
  280. else this.allocated.push(item)
  281. }
  282. },
  283. async handleNodeClick(data) {
  284. this.checkAll = false
  285. this.isIndeterminate = false
  286. this.regionId = data.id
  287. this.saleRegionName = data.regionDesc
  288. await this.handleDetail()
  289. const [err, res] = await to(regionApi.getRegionDetailList({ regionId: data.id }))
  290. if (err) return
  291. this.flatOptions = res.data.list || []
  292. this.flatOptions.forEach((item) => (item.saleRegionName = data.regionDesc))
  293. let obj = []
  294. for (const item of this.flatOptions) {
  295. const target = obj.findIndex((each) => each.custProvinceId == item.custProvinceId)
  296. const newItem = { ...item, allocator: [] }
  297. const allocator = this.allocated.find((each) => each.cityId == item.custCityId)
  298. if (allocator) {
  299. newItem.allocator.push(allocator.userName)
  300. }
  301. if (target > -1) {
  302. obj[target].children.push(newItem)
  303. } else {
  304. obj.push({
  305. checkAll: false,
  306. isIndeterminate: false,
  307. custProvinceId: item.custProvinceId,
  308. custProvince: item.custProvince,
  309. children: [newItem],
  310. })
  311. }
  312. }
  313. this.options = obj
  314. this.handleChange(this.form.checkList)
  315. },
  316. // 筛选节点
  317. filterNode(value, data) {
  318. if (!value) return true
  319. return data[this.defaultProps.label].indexOf(value) !== -1
  320. },
  321. handleClose() {
  322. // this.form = {
  323. // userId: 0,
  324. // checkList: [],
  325. // }
  326. this.getSndList()
  327. },
  328. handleCheckAllChange(val) {
  329. this.form.checkList = val ? this.flatOptions.map((item) => item.custCityId) : []
  330. this.options.forEach((item) => {
  331. item.checkAll = val
  332. })
  333. this.isIndeterminate = false
  334. this.$forceUpdate()
  335. this.save()
  336. },
  337. handleSndCheckAllChange(val, item) {
  338. item.isIndeterminate = false
  339. for (let i = 0; i < item.children.length; i++) {
  340. const index = this.form.checkList.findIndex((each) => each == item.children[i].custCityId)
  341. if (index > -1) {
  342. this.form.checkList.splice(index, 1)
  343. i--
  344. }
  345. }
  346. const arr = item.children.map((each) => each.custCityId)
  347. if (val) this.form.checkList = [...this.form.checkList, ...arr]
  348. this.$forceUpdate()
  349. this.save()
  350. },
  351. handleChange(val, item) {
  352. if (item) {
  353. let count = 0
  354. for (const child of item.children) {
  355. console.log(child, child.custCityId, this.form.checkList, '---')
  356. if (this.form.checkList.includes(child.custCityId)) count++
  357. }
  358. item.checkAll = count == item.children.length
  359. item.isIndeterminate = count > 0 && count < item.children.length
  360. this.save()
  361. } else {
  362. for (const pro of this.options) {
  363. let count = 0
  364. pro.children.forEach((city) => {
  365. if (this.form.checkList.includes(city.custCityId)) count++
  366. })
  367. pro.checkAll = count == pro.children.length
  368. pro.isIndeterminate = count > 0 && count < pro.children.length
  369. }
  370. }
  371. this.$forceUpdate()
  372. this.isIndeterminate = val.length > 0 && val.length < this.flatOptions.length
  373. this.checkAll = val.length == this.flatOptions.length && val.length > 0
  374. },
  375. async save() {
  376. this.dialogLoading = true
  377. let params = { ...this.form }
  378. params.saleRegionId = this.regionId
  379. params.saleRegionName = this.saleRegionName
  380. let arr = []
  381. for (const item of params.checkList) {
  382. const obj = this.flatOptions.find((each) => each.id == item)
  383. if (obj)
  384. arr.push({
  385. saleRegionId: obj.regionId,
  386. saleRegionName: obj.saleRegionName,
  387. provinceId: obj.custProvinceId,
  388. provinceName: obj.custProvince,
  389. cityId: obj.custCityId,
  390. cityName: obj.custCity,
  391. })
  392. }
  393. params.regions = arr
  394. const [err, res] = await to(regAuthApi.saveRegion(params))
  395. if (err) return (this.dialogLoading = false)
  396. this.$message.success(res.msg)
  397. this.dialogLoading = false
  398. },
  399. },
  400. }
  401. </script>
  402. <style lang="scss" scoped>
  403. .side-layout {
  404. height: 600px;
  405. }
  406. .el-icon-check {
  407. font-size: 18px;
  408. color: #67c23a;
  409. font-weight: bold;
  410. }
  411. .vab-theme-blue-black .side-layout .tree-table {
  412. padding: 10px 20px;
  413. .province {
  414. margin: 20px 0;
  415. line-height: 24px;
  416. h3 {
  417. padding: 10px;
  418. border-radius: 5px;
  419. background: #eee;
  420. }
  421. .check-container {
  422. padding-left: 8px;
  423. line-height: 30px;
  424. }
  425. }
  426. }
  427. </style>