index.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. <template>
  2. <div class="store_page">
  3. <div class="page_header">
  4. <div class="header_left">
  5. <div class="header_label">用户管理</div>
  6. </div>
  7. <div class="header_right">
  8. <userBox />
  9. </div>
  10. </div>
  11. <div class="filter_box">
  12. <div class="filter_left">
  13. <div class="filter_item">
  14. <label>手机号:</label>
  15. <Input v-model="phone" icon="ios-search" placeholder="请输入" clearable @on-change="handleSearch" />
  16. </div>
  17. <div class="filter_item">
  18. <label>经销商:</label>
  19. <Select v-model="storeId" clearable @on-change="handleSearch">
  20. <Option v-for="item in storeList" :key="item.id" :value="item.id" :label="item.name">
  21. {{ item.name }}</Option>
  22. </Select>
  23. </div>
  24. </div>
  25. <div class="filter_right">
  26. <div class="add_btn" @click="handleAdd">新增用户</div>
  27. </div>
  28. </div>
  29. <div class="page_content" ref="tableContent">
  30. <div class="table_content">
  31. <!--表格-->
  32. <Table row-key="id" :loading="loading" :height="tableHeight" :columns="tableColumns" :data="tableData">
  33. </Table>
  34. <!-- 分页 -->
  35. <Page :total="total" :current="currentPage" show-total show-sizer @on-change="handleCurrentChange"
  36. @on-page-size-change="handlePageSize" :page-size-opts="pageSizeOpts" />
  37. </div>
  38. </div>
  39. <Modal scrollable v-model="showDialog" width="442">
  40. <div slot="header">
  41. <p>{{ formValidate.id ? '编辑用户' : '新增用户' }}</p>
  42. </div>
  43. <Form ref="formValidate" :model="formValidate" :rules="ruleValidate" :label-width="120">
  44. <FormItem :label="`手机号:`" prop="phone">
  45. <Input v-model="formValidate.phone" :maxlength="11" placeholder="请输入"></Input>
  46. </FormItem>
  47. <FormItem label="经销商:" prop="storeId">
  48. <Select v-model="formValidate.storeId" transfer @on-change="getWxcpMemberList">
  49. <Option v-for="item in storeList" :key="item.id" :value="item.id" :label="item.name">
  50. {{ item.name }}</Option>
  51. </Select>
  52. </FormItem>
  53. <FormItem :label="`短信模板ID:`">
  54. <Input v-model="formValidate.smsAfterCallParam" placeholder="请输入" />
  55. </FormItem>
  56. <FormItem label="绑定企微:" v-if="formValidate.storeId">
  57. <Select v-model="formValidate.wxcpMemberId" transfer clearable @on-clear="handleClearWxcpMemberId">
  58. <Option v-for="item in wxcpMemberList" :key="item.memberId" :value="item.memberId"
  59. :label="item.name">
  60. {{ item.name }}</Option>
  61. </Select>
  62. </FormItem>
  63. </Form>
  64. <template slot="footer">
  65. <Button class="cancel-btn" @click="showDialog = false">取消</Button>
  66. <Button class="confirm-btn" type="primary" :loading="btnLoading"
  67. @click="handleOk('formValidate')">确定</Button>
  68. </template>
  69. </Modal>
  70. </div>
  71. </template>
  72. <script>
  73. import { mapState } from 'vuex'
  74. import { debounce, checkPhone } from '@/utils'
  75. import userBox from '@/pages/platform/components/userBox'
  76. import tablePageMixin from '_m/tablePage.mixin'
  77. export default {
  78. mixins: [tablePageMixin],
  79. components: {
  80. userBox
  81. },
  82. data() {
  83. return {
  84. loading: false,
  85. btnLoading: false,
  86. tableData: [],
  87. showDialog: false,
  88. formValidate: {
  89. id: undefined,
  90. phone: undefined,
  91. smsAfterCallParam: undefined,
  92. storeId: undefined,
  93. wxcpMemberId: undefined,
  94. },
  95. ruleValidate: {
  96. phone: [
  97. { required: true, validator: this.handleValidatePhone.bind(this), trigger: 'blur' }
  98. ],
  99. // smsAfterCallParam: [
  100. // { type: 'string', required: true, message: '请输入', trigger: 'blur' }
  101. // ],
  102. storeId: [
  103. { type: 'number', required: true, message: '请选择', trigger: 'change' }
  104. ],
  105. },
  106. phone: null,
  107. tableHeight: 0,
  108. storeList: [],
  109. storeId: null,
  110. wxcpMemberList: [],
  111. }
  112. },
  113. computed: {
  114. ...mapState({
  115. user: (state) => state.user, //
  116. }),
  117. tableColumns() {
  118. let columns = [
  119. {
  120. title: '手机号',
  121. minWidth: 140,
  122. key: 'phone',
  123. align: 'left',
  124. },
  125. {
  126. title: '经销商',
  127. minWidth: 140,
  128. key: 'store',
  129. align: 'left',
  130. render: (h, params) => {
  131. const { row } = params
  132. let text = (row.store && row.store.name) || ''
  133. return h('span', undefined, text)
  134. },
  135. },
  136. {
  137. title: '短信模板ID',
  138. minWidth: 140,
  139. key: 'smsAfterCallParam',
  140. align: 'left'
  141. },
  142. {
  143. title: '绑定企微',
  144. minWidth: 140,
  145. key: 'wxcpMemberName',
  146. align: 'left'
  147. },
  148. {
  149. title: '状态',
  150. minWidth: 140,
  151. key: 'status',
  152. align: 'left',
  153. render: (h, params) => {
  154. const { row } = params
  155. let text = ''
  156. if (row.userStatus === 1) {
  157. return (
  158. h('span', {
  159. style: {
  160. color: '#35E89A'
  161. }
  162. }, '正常')
  163. )
  164. } else if (row.userStatus === -1) {
  165. return (
  166. h('span', {
  167. style: {
  168. color: '#cccccc'
  169. }
  170. }, '已禁用')
  171. )
  172. } else {
  173. return (
  174. h('span', {
  175. style: {
  176. color: '#999999'
  177. }
  178. }, '未激活')
  179. )
  180. }
  181. }
  182. },
  183. {
  184. title: '操作',
  185. align: 'left',
  186. width: 130,
  187. render: (h, params) => {
  188. const array = []
  189. const { row, index } = params
  190. array.push(h('div', {
  191. style: {
  192. color: '#1677FF',
  193. cursor: 'pointer',
  194. },
  195. on: {
  196. click: (event) => {
  197. this.handleEdit(row)
  198. }
  199. }
  200. }, '编辑'))
  201. if (row.userStatus !== -1) {
  202. array.push(h('div', {
  203. style: {
  204. color: '#FF4E4E',
  205. cursor: 'pointer',
  206. },
  207. on: {
  208. click: (event) => {
  209. this.handleDelete(row)
  210. }
  211. }
  212. }, '禁用'))
  213. }
  214. return h('div', {
  215. style: {
  216. display: 'flex',
  217. lineHeight: '17px',
  218. gap: '5px 30px',
  219. whiteSpace: 'nowrap'
  220. },
  221. }, array)
  222. }
  223. }
  224. ]
  225. return columns
  226. }
  227. },
  228. created() {
  229. this.handleCalcTableHeight()
  230. const resize = debounce(() => {
  231. this.handleCalcTableHeight()
  232. }, 300)
  233. window.addEventListener('resize', resize)
  234. this.$once('hook:beforeDestroy', () => {
  235. window.removeEventListener('resize', resize)
  236. })
  237. this.getStoreList()
  238. this.initData()
  239. },
  240. methods: {
  241. handleValidatePhone(rule, value, callback) {
  242. if (!value) {
  243. //callback(new Error('请输入手机号'))
  244. callback()
  245. } else if (!checkPhone(value)) {
  246. callback(new Error('请输入正确的手机号'))
  247. } else {
  248. callback()
  249. }
  250. },
  251. handleCalcTableHeight() {
  252. this.$nextTick().then(() => {
  253. const tableContent = this.$refs['tableContent']
  254. let tableContentHeight = 0
  255. if (tableContent) {
  256. // 普通的 html节点
  257. tableContentHeight = tableContent.getBoundingClientRect().height
  258. }
  259. this.tableHeight = tableContentHeight - (32 + 40 + 58)
  260. })
  261. },
  262. handleSearch() {
  263. this.currentPage = 1
  264. this.initData()
  265. },
  266. initData() {
  267. this.$http.post(`/call/api/user/m/findListByPage?page=${this.currentPage}&pageCount=${this.pageSize}`, {
  268. phone: this.phone,
  269. storeId: this.storeId
  270. }).then((response) => {
  271. let { data, code, msg } = response
  272. if (code === 1) {
  273. this.tableData = data.records
  274. this.total = data.total
  275. } else {
  276. this.$Notice.warning({
  277. desc: msg
  278. })
  279. }
  280. })
  281. },
  282. handleAdd() {
  283. this.showDialog = true
  284. this.formValidate = {
  285. id: undefined,
  286. phone: undefined,
  287. smsAfterCallParam: undefined,
  288. storeId: undefined,
  289. wxcpMemberId: undefined,
  290. }
  291. if (this.formValidate.storeId) {
  292. this.getWxcpMemberList()
  293. }
  294. },
  295. handleEdit(row) {
  296. this.showDialog = true
  297. this.formValidate = {
  298. id: row.id,
  299. phone: row.phone,
  300. smsAfterCallParam: row.smsAfterCallParam || undefined,
  301. storeId: (row.store && row.store.id) || undefined,
  302. wxcpMemberId: row.wxcpMemberId || undefined,
  303. }
  304. if (this.formValidate.storeId) {
  305. this.getWxcpMemberList()
  306. }
  307. },
  308. getStoreList() {
  309. this.$http.post(`/call/api/call-store/findListByPage?page=1&pageCount=1000`, {}).then((response) => {
  310. const { code, data, msg } = response
  311. if (code === 1) {
  312. this.storeList = data.records
  313. }
  314. })
  315. },
  316. getWxcpMemberList() {
  317. this.$http.get(`/call/api/user/m/member-options`, {
  318. params: {
  319. tenancyId: this.storeList.find(item => item.id === this.formValidate.storeId).tenancyId,
  320. page: 1,
  321. pageCount: 1000
  322. }
  323. }).then((response) => {
  324. const { code, data, msg } = response
  325. if (code === 1) {
  326. this.wxcpMemberList = data.records || []
  327. }
  328. })
  329. },
  330. handleClearWxcpMemberId() {
  331. this.formValidate.wxcpMemberId = null
  332. },
  333. handleOk(name) {
  334. this.$refs[name].validate((valid) => {
  335. if (valid) {
  336. this.btnLoading = true
  337. this.$http.post(`/call/api/user/m/${this.formValidate.id ? 'updateUser' : 'addUser'}`, {
  338. ...this.formValidate,
  339. }).then((response) => {
  340. let { code, msg } = response
  341. if (code === 1) {
  342. this.$Notice.success({
  343. title: msg,
  344. })
  345. this.showDialog = false
  346. this.handleSearch()
  347. } else {
  348. this.$Notice.warning({
  349. desc: msg
  350. })
  351. }
  352. }).finally(() => {
  353. this.btnLoading = false
  354. })
  355. }
  356. })
  357. },
  358. handleDelete(row) {
  359. this.$Modal.confirm({
  360. title: '提示',
  361. content: `<div class="confirm_cont text_conter">确认禁用该用户?</div>`,
  362. onOk: () => {
  363. this.$http.post(`/call/api/user/m/forbiddenUser`, {
  364. id: row.id
  365. }).then((response) => {
  366. let { code, msg } = response
  367. if (code === 1) {
  368. this.$Notice.success({
  369. title: msg,
  370. })
  371. this.handleSearch()
  372. } else {
  373. this.$Notice.warning({
  374. desc: msg
  375. })
  376. }
  377. })
  378. }
  379. })
  380. },
  381. },
  382. }
  383. </script>
  384. <style lang="less" scoped>
  385. .store_page {
  386. .page_content {}
  387. }
  388. </style>