企微助手 ,仓库名 短剧

createGroupCode.vue 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  1. <template>
  2. <div class="create-wrap">
  3. <!-- S 返回 -->
  4. <div class="backBox" @click="$router.go(-1)">
  5. <div class="back">
  6. <i class="el-icon-back" />
  7. <span>返回</span>
  8. </div>
  9. </div>
  10. <!-- E 返回 -->
  11. <!-- S 表单内容 -->
  12. <div v-loading="loading" class="bg-ffffff createMassMsg" style="padding: 15px 30px;">
  13. <!-- S 基础信息 -->
  14. <h3 class="bigTitle">基础信息</h3>
  15. <div class="regulations">
  16. <label><em>*</em>渠道名称</label>
  17. <el-input
  18. v-model.trim="groupForm.name"
  19. placeholder="请输入渠道名称"
  20. style="width:300px"
  21. maxlength="32"
  22. size="small"
  23. show-word-limit
  24. clearable
  25. />
  26. </div>
  27. <div class="regulations" style="align-items: center">
  28. <label><em style="opacity: 0;">*</em>选择分组</label>
  29. <el-select
  30. v-model="groupForm.group_id"
  31. size="small"
  32. clearable
  33. filterable
  34. placeholder="请选择分组"
  35. style="width: 300px"
  36. >
  37. <el-option v-for="item in groupOptions" :key="item.id" :label="item.name" :value="item.id" />
  38. </el-select>
  39. <div class="newGroupCss" @click="$refs.dialogGroupRef.dialogVisible = true">新建分组</div>
  40. </div>
  41. <div class="regulations">
  42. <label><em>*</em>选择群聊</label>
  43. <chatGroupOptions
  44. ref="chatGroupOptions"
  45. style="margin: 0;"
  46. width="300px"
  47. :chatListResult="groupForm.chat_id_list"
  48. @change="onChangeChatGroup"
  49. />
  50. </div>
  51. <div class="regulations">
  52. <label><em style="opacity: 0;">*</em>进群方式</label>
  53. <el-radio-group v-model="groupForm.join_type">
  54. <el-radio :label="1">顺序进群</el-radio>
  55. <el-radio :label="2">随机进群</el-radio>
  56. </el-radio-group>
  57. </div>
  58. <div class="regulations" style="align-items: center">
  59. <label>渠道群管理</label>
  60. <div @click="handleShowLimitTips" class="limitBox">
  61. <el-switch
  62. v-model="groupForm.is_limit"
  63. :disabled="!groupForm.chat_id_list.length"
  64. active-color="#13ce66"
  65. inactive-color="#ddd"
  66. :active-value="1"
  67. :inactive-value="0"
  68. @change="onChangeSwitchLimit"
  69. />
  70. </div>
  71. </div>
  72. <!-- S 群管理表格 开启"渠道群管理"时显示 -->
  73. <div v-show="groupForm.is_limit" class="listBox">
  74. <el-table border :data="groupForm.chat_id_list" tooltip-effect="dark" :header-cell-style="()=>{return { backgroundColor: '#f9f9f9 !important' }}" style="width: 100%">
  75. <el-table-column label="拖拽排序" width="80" align="center">
  76. <template slot-scope="{ row }">
  77. <div class="drag-handler">
  78. <i class="el-icon-rank" />
  79. </div>
  80. </template>
  81. </el-table-column>
  82. <el-table-column label="群名称" align="center" prop="name" />
  83. <el-table-column label="人数上限" align="center">
  84. <template slot="header" slot-scope="scope">
  85. <em style="color: #ff0000;">*</em>人数上限
  86. </template>
  87. <template slot-scope="{ row }">
  88. <el-input size="mini" v-model="row.user_limit" />
  89. </template>
  90. </el-table-column>
  91. <el-table-column label="状态" align="center">
  92. <template slot-scope="{ row }">
  93. <el-switch
  94. v-model="row.status"
  95. :active-value="1"
  96. :inactive-value="0"
  97. active-color="#13ce66"
  98. inactive-color="#ddd"
  99. />
  100. </template>
  101. </el-table-column>
  102. <el-table-column label="操作" align="center" width="80px">
  103. <template slot-scope="{ row, $index }">
  104. <div class="c-00B38A pointer lMar8" @click="onClickDelChat(row, $index)">删除</div>
  105. </template>
  106. </el-table-column>
  107. </el-table>
  108. </div><!-- E 群管理表格 -->
  109. <!-- E 基础信息 -->
  110. <!-- S 客服设置 -->
  111. <div class="line" style="margin-top:20px"></div>
  112. <h3 class="bigTitle">客服设置</h3>
  113. <div class="regulations" style="align-items: center">
  114. <label><em>*</em>生效成员</label>
  115. <self-customerservice
  116. title=''
  117. width="300px"
  118. source="channelCode"
  119. ref="selfKefu"
  120. style="margin:0;"
  121. :afferent_users="groupForm.user_list"
  122. @customerDefine="onChangeUserList"
  123. />
  124. <div class="user-tips">当客户点击客服时,会引导客户随机添加一名客服成员的企业微信</div>
  125. </div>
  126. <div class="regulations">
  127. <label><em>*</em>引导话术</label>
  128. <el-input
  129. v-model.trim="groupForm.leading_words"
  130. placeholder="请输入引导话术"
  131. style="width:300px"
  132. maxlength="20"
  133. size="small"
  134. show-word-limit
  135. clearable
  136. />
  137. </div>
  138. <!-- E 客服设置 -->
  139. <!-- S 按钮 -->
  140. <div class="line" style="margin-top:20px" />
  141. <div style="margin-top: 20px"><el-button type="primary" @click="onClickSave">保存</el-button></div>
  142. <!-- E 按钮 -->
  143. </div>
  144. <!-- E 表单内容 -->
  145. <!-- S 新建分组 - 弹框 -->
  146. <dialogGroup
  147. ref="dialogGroupRef"
  148. :propsData="{ dialogTitle:'新建' }"
  149. @init="handleGetGroupOptions"
  150. />
  151. <!-- E 新建分组 - 弹框 -->
  152. </div>
  153. </template>
  154. <script>
  155. import dialogGroup from './components/dialogGroup.vue'
  156. import chatGroupOptions from './components/chatGroupOptions.vue'
  157. import selfCustomerservice from '@/components/assembly/screen/customerService.vue'
  158. import Sortable from 'sortablejs'
  159. import _lodash from 'lodash'
  160. export default {
  161. name: "createGroupCode",
  162. components: {
  163. dialogGroup,
  164. chatGroupOptions,
  165. selfCustomerservice,
  166. },
  167. data() {
  168. return {
  169. loading: false,
  170. groupOptions: [], // 分组选项
  171. groupForm: {
  172. name: '', // 渠道名称
  173. group_id: '', // 分组id
  174. chat_id_list: [], // 客户群列表
  175. join_type: 1, // 进群方式
  176. is_limit: 0, // 渠道群管理(是否设置群上限)
  177. user_list: [], // 客服(生效成员) 数组回显 'a,b' => ['a', 'b']
  178. leading_words: '', // 引导话术
  179. },
  180. rule_id: this.$route.query.id || '', // 群活码id
  181. detailFromApi: Object.freeze({}), // 后端返回的详情完整数据
  182. }
  183. },
  184. computed: {
  185. // 当前操作是否为"编辑"
  186. isEdit() {
  187. const { id = '', type = '' } = this.$route.query
  188. return id && type === 'edit'
  189. },
  190. // 当前操作是否为"复制"
  191. isCopy() {
  192. const { id = '', type = '' } = this.$route.query
  193. return id && type === 'copy'
  194. },
  195. },
  196. created() {
  197. // 获取分组列表
  198. this.handleGetGroupOptions()
  199. // 编辑、复制 => 获取群活码详情
  200. if (this.isEdit || this.isCopy) {
  201. this.handleGetDetail()
  202. }
  203. },
  204. mounted() {
  205. // 表格行拖拽
  206. this.handleRowDrop()
  207. },
  208. methods: {
  209. // 获取群活码详情数据
  210. async handleGetDetail() {
  211. try {
  212. this.loading = true
  213. const { data: res = {} } = await this.$axios.get(this.URL.BASEURL + this.URL.groupCode_ruleDetail, {
  214. params: { rule_id: this.rule_id }
  215. })
  216. if (res && res.errno == 0) {
  217. const { rst = {} } = res
  218. // 备份接口数据
  219. this.detailFromApi = Object.freeze(_lodash.cloneDeep(rst))
  220. // 回显数据
  221. this.setGroupFormData(rst)
  222. } else if (res.errno != 4002) {
  223. this.$message.warning(res.err)
  224. this.$router.go(-1)
  225. }
  226. } catch (error) {
  227. console.log('error => ', error)
  228. this.loading = false
  229. } finally {
  230. // this.loading = false
  231. }
  232. },
  233. // 回显详情数据
  234. async setGroupFormData(detail) {
  235. try {
  236. // 基础属性回显
  237. const keys = ['name', 'group_id', 'chat_id_list', 'join_type', 'is_limit', 'leading_words']
  238. keys.forEach(k => {
  239. this.groupForm[k] = detail[k]
  240. })
  241. // 回显生效成员
  242. this.groupForm.user_list = detail.user_list.split(',')
  243. await this.$nextTick()
  244. this.$refs.selfKefu && this.$refs.selfKefu.handleCreatedFn()
  245. // 回显群聊列表"群名称"
  246. const { data: res = {} } = await this.$axios.get(this.URL.BASEURL + this.URL.groupCode_chatGroupList, {
  247. params: { keyword: '' }
  248. })
  249. if (res && res.errno == 0 && Array.isArray(res.rst)) {
  250. detail.chat_id_list.forEach((item, idx) => {
  251. const isFound = res.rst.find(chat => chat.chat_id === item.chat_id)
  252. if (isFound) detail.chat_id_list[idx].name = isFound.name
  253. })
  254. this.groupForm.chat_id_list = [...detail.chat_id_list]
  255. }
  256. } catch (error) {
  257. console.log('error => ', error)
  258. } finally {
  259. this.loading = false
  260. }
  261. },
  262. // 注册表格行拖拽事件
  263. handleRowDrop() {
  264. const tbody = document.querySelector('.el-table__body-wrapper tbody')
  265. const _this = this
  266. Sortable.create(tbody, {
  267. handle: '.drag-handler',
  268. onEnd({ newIndex, oldIndex }) {
  269. if (newIndex == oldIndex) return
  270. _this.groupForm.chat_id_list.splice(
  271. newIndex,
  272. 0,
  273. _this.groupForm.chat_id_list.splice(oldIndex, 1)[0]
  274. )
  275. const newArray = _this.groupForm.chat_id_list.slice(0)
  276. _this.groupForm.chat_id_list = []
  277. _this.$nextTick(() => {
  278. _this.groupForm.chat_id_list = newArray
  279. _this.$refs.chatGroupOptions.chatListSelected = [] // 重置群聊组件"已选列表"
  280. })
  281. }
  282. })
  283. },
  284. // 点击保存按钮
  285. async onClickSave() {
  286. console.log('onClickSave this.groupForm => ', JSON.parse(JSON.stringify(this.groupForm)))
  287. try {
  288. // 表单判空校验
  289. await this.handleFormValidate()
  290. // 获取请求地址 & 参数
  291. const { url, params } = this.handleGetParams()
  292. console.log('params => ', JSON.parse(JSON.stringify(params)))
  293. console.log('url => ', url)
  294. this.loading = true
  295. const { data: res } = await this.$axios.post(url, params)
  296. console.log('res => ', res)
  297. if (res && res.errno == 0) {
  298. this.$message.success('保存成功')
  299. this.$router.go(-1)
  300. } else if (res.errno != 4002) {
  301. this.$message.warning(res.err || '操作失败')
  302. }
  303. } catch (error) {
  304. console.log('error => ', error)
  305. } finally {
  306. this.loading = false
  307. }
  308. },
  309. // 表单校验
  310. handleFormValidate() {
  311. const { name, chat_id_list, is_limit, user_list, leading_words } = this.groupForm
  312. const isEmpty = chat_id_list.some(chat => Number(chat.user_limit) === 0)
  313. return new Promise((resolve, reject) => {
  314. if (!name) {
  315. this.$message.warning('请输入渠道名称')
  316. reject('表单校验未通过')
  317. } else if (!chat_id_list.length) {
  318. this.$message.warning('请选择群聊')
  319. reject('表单校验未通过')
  320. } else if (is_limit === 1 && isEmpty) {
  321. this.$message.warning('请输入人数上限')
  322. reject('表单校验未通过')
  323. } else if (!user_list.length) {
  324. this.$message.warning('请选择生效成员')
  325. reject('表单校验未通过')
  326. } else if (!leading_words) {
  327. this.$message.warning('请输入引导话术')
  328. reject('表单校验未通过')
  329. } else {
  330. resolve('表单校验通过')
  331. }
  332. })
  333. },
  334. // 整理请求参数
  335. handleGetParams() {
  336. const url = `${this.URL.BASEURL}${this.URL[this.isEdit ? 'groupCode_editRule' : 'groupCode_setRule']}`
  337. const { name, group_id, join_type, is_limit, user_list, leading_words, chat_id_list } = this.groupForm
  338. // 删除后端返回的 sort 属性
  339. const chatIdList = chat_id_list.map(c => {
  340. delete c.sort
  341. return {...c}
  342. })
  343. const params = {
  344. name,
  345. group_id,
  346. join_type,
  347. is_limit,
  348. user_list: user_list.join(','),
  349. leading_words,
  350. }
  351. if (this.isEdit) {
  352. params.rule_id = this.rule_id
  353. params.config_id = this.detailFromApi.config_id
  354. // 接口返回的原始数据 与 页面显示的数据比对
  355. this.detailFromApi.chat_id_list.forEach((item, idx) => {
  356. const isFound = chatIdList.find(c => c.chat_id === item.chat_id)
  357. // 如果原始数据 在页面中"没"找到 => 证明"已删除"
  358. if (!isFound) {
  359. item.enable = 0 // 参数写为0 并追加到数组末
  360. chatIdList.push(item)
  361. }
  362. })
  363. }
  364. params.chat_id_list = JSON.stringify(chatIdList)
  365. return { url, params }
  366. },
  367. // 获取分组列表
  368. async handleGetGroupOptions() {
  369. const { data: res = {} } = await this.$axios.get(this.URL.BASEURL + this.URL.channel_groupList, {
  370. params: {
  371. type: 2, // 1:渠道活码 2:群活码
  372. page: 1,
  373. pagesize: 500,
  374. }
  375. })
  376. if (res && res.errno == 0) {
  377. this.groupOptions = res.rst.data
  378. } else if (res.errno != 4002) {
  379. this.$message.warning(res.err)
  380. }
  381. },
  382. // 渠道群管理提示
  383. handleShowLimitTips() {
  384. if (!this.groupForm.chat_id_list.length) {
  385. this.$message.error('请先选择群聊')
  386. }
  387. },
  388. onChangeSwitchLimit(){
  389. if (!this.groupForm.chat_id_list.length) {
  390. this.$message.error('请先选择群聊')
  391. return
  392. }
  393. },
  394. // 渠道群管理列表 - 删除
  395. onClickDelChat(row, idx) {
  396. this.groupForm.chat_id_list.splice(idx, 1)
  397. this.$refs.chatGroupOptions.chatListSelected = [] // 重置群聊组件"已选列表"
  398. // 列表为空 => 关闭"渠道群管理"按钮
  399. if (!this.groupForm.chat_id_list.length) {
  400. this.groupForm.is_limit = 0
  401. }
  402. },
  403. // 监听"选择客服"(生效成员)变化
  404. onChangeUserList(val) {
  405. this.groupForm.user_list = Array.isArray(val) && val.length ? val : []
  406. },
  407. // 监听"选择群聊"变化
  408. onChangeChatGroup(selectedChat) {
  409. const { chat_id_list: currentChats } = this.groupForm
  410. // 整理数据结构 => 初始化新选择的群聊数据
  411. const newChats = selectedChat.map(s => ({
  412. chat_id: s.chat_id,
  413. name: s.name,
  414. user_limit: 0,
  415. enable: 1,
  416. status: 1,
  417. }))
  418. // 在"当前页面的群聊列表"中查找是否已存在 => 去重后得到当前页面新群聊列表
  419. newChats.forEach((newChat, idx) => {
  420. const isFoundChat = currentChats.find(c => newChat.chat_id === c.chat_id)
  421. if (isFoundChat) newChats.splice(idx, 1, isFoundChat)
  422. })
  423. this.groupForm.chat_id_list = [...newChats]
  424. console.log('this.groupForm.chat_id_list => ', this.groupForm.chat_id_list)
  425. },
  426. },
  427. }
  428. </script>
  429. <style lang="scss">
  430. .appendOnly{
  431. .el-input-group__append{
  432. padding: 0 20px;
  433. }
  434. }
  435. .limitBox{
  436. .el-switch.is-disabled .el-switch__core{
  437. cursor: pointer;
  438. }
  439. }
  440. </style>
  441. <style lang="scss" scoped>
  442. .backBox {
  443. background: #ffffff;
  444. padding: 15px;
  445. margin-bottom: 10px;
  446. .back {
  447. font-size: 16px;
  448. font-weight: bold;
  449. color: #333333;
  450. cursor: pointer;
  451. display: inline-block;
  452. }
  453. }
  454. .bigTitle {
  455. color: #333333;
  456. font-size: 16px;
  457. line-height: 22px;
  458. font-weight: bold;
  459. }
  460. .createMassMsg{
  461. width: 100%;
  462. min-height: 400px;
  463. .line {
  464. width: 730px;
  465. height: 1px;
  466. background: #e9e9e9;
  467. margin: 40px 0 20px;
  468. }
  469. .regulations {
  470. display: flex;
  471. align-items: flex-start;
  472. margin-top: 28px;
  473. label {
  474. width: 100px;
  475. color: #333333;
  476. font-size: 14px;
  477. line-height: 28px;
  478. em {
  479. color: #ff0000;
  480. }
  481. }
  482. .user-tips {
  483. color: #AAAAAA;
  484. font-size: 13px;
  485. margin-left: 20px;
  486. }
  487. }
  488. .regulations2 {
  489. label {
  490. width: 70px;
  491. line-height: 30px;
  492. }
  493. }
  494. }
  495. .detail_status{
  496. line-height: 30px;
  497. height: 30px;
  498. padding: 3px 10px;
  499. border-radius: 21px;
  500. font-size: 12px;
  501. margin-left: 20px;
  502. }
  503. .status_wait{
  504. background: #FFF2ED;
  505. border: 1px solid #FF864F;
  506. color: #FF864F;
  507. }
  508. .status_del{
  509. background: #FFF5F5;
  510. border: 1px solid #FF604D;
  511. color: #FF604D;
  512. }
  513. .status_fail{
  514. background: #F4F4F4;
  515. border: 1px solid #ADADAD;
  516. color: #ADADAD;
  517. }
  518. .status_ing{
  519. background: #F4FAFF;
  520. border: 1px solid #007AFE;
  521. color: #007AFE;
  522. }
  523. .status_success{
  524. background: #EEFFF3;
  525. border: 1px solid #00B38A;
  526. color: #00B38A;
  527. }
  528. .splitLine {
  529. width: 100%;
  530. height: 10px;
  531. background: #f5f6f8;
  532. }
  533. .listBox{
  534. margin-left: 90px;
  535. width: 730px;
  536. margin-top: 10px;
  537. background-color: #fbfbfb;
  538. padding: 10px;
  539. }
  540. .newGroupCss{
  541. color: #00b38a;
  542. font-size: 14px;
  543. margin-left: 10px;
  544. cursor: pointer;
  545. }
  546. .add_welcom_hint {
  547. color: #00b38a;
  548. font-size: 16px;
  549. font-weight: bold;
  550. margin-left: 90px;
  551. margin-top: 10px;
  552. margin-bottom: 20px;
  553. cursor: pointer;
  554. display: inline-block;
  555. }
  556. .drag-handler {
  557. cursor: move;
  558. }
  559. </style>