index.vue 44 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079
  1. <template>
  2. <div class="app-container">
  3. <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="120px">
  4. <el-form-item label="客服" prop="customerRoles">
  5. <div @click="openCustomerPage(0)"
  6. style="cursor: pointer; border: 1px solid #e6e6e6; background-color: white; overflow: hidden; flex-grow: 1;width: 250px">
  7. <div style="min-height: 35px; max-height: 200px; overflow-y: auto;">
  8. <el-tag type="success"
  9. closable
  10. :disable-transitions="false"
  11. v-for="list in appCustomerSearchConfig.selected"
  12. :key="list.id"
  13. @close="handleCloseCustomer(list, 0)"
  14. style="margin: 3px;"
  15. >{{ list.memberName }}
  16. </el-tag>
  17. </div>
  18. </div>
  19. </el-form-item>
  20. <el-form-item label="创建时间" prop="createTime">
  21. <el-date-picker clearable size="small" v-model="queryParams.createTime" type="date"
  22. value-format="yyyy-MM-dd" placeholder="选择创建时间">
  23. </el-date-picker>
  24. </el-form-item>
  25. <el-form-item label="是否发送欢迎语" prop="sendStatus">
  26. <el-select v-model="queryParams.sendStatus" placeholder="请选择" clearable size="small">
  27. <el-option label="是" :value="1"/>
  28. <el-option label="否" :value="0"/>
  29. </el-select>
  30. </el-form-item>
  31. <el-form-item>
  32. <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
  33. <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
  34. <el-button
  35. type="primary"
  36. plain
  37. size="mini"
  38. icon="el-icon-plus"
  39. @click="handleAdd"
  40. v-hasPermi="['app:welcome:add']"
  41. >
  42. 新增
  43. </el-button>
  44. </el-form-item>
  45. </el-form>
  46. <el-row :gutter="10" class="mb8">
  47. <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
  48. </el-row>
  49. <el-table v-loading="loading" :data="welcomeList" border>
  50. <el-table-column label="消息内容" align="left" prop="welcomeText" min-width="200">
  51. <template slot-scope="scope">
  52. <el-tooltip class="item" effect="dark" :content="scope.row.welcomeText" placement="top">
  53. <div
  54. style="display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; overflow: hidden; text-overflow: ellipsis;">
  55. <span>{{ scope.row.welcomeText }}</span>
  56. </div>
  57. </el-tooltip>
  58. </template>
  59. </el-table-column>
  60. <el-table-column label="使用客服" align="center" prop="customerRoles" min-width="250">
  61. <template slot-scope="scope">
  62. <div v-for="customerRole in (scope.row.customerRoles ? scope.row.customerRoles.split(',') : [])" :key="customerRole" style="display: inline;"
  63. class="text-container">
  64. <el-tag type="success" style="margin: 3px;">{{ customerRole }}</el-tag>
  65. </div>
  66. </template>
  67. </el-table-column>
  68. <el-table-column label="发送状态" align="center" prop="sendStatus" min-width="120">
  69. <template slot-scope="scope">
  70. <el-switch v-model="scope.row.sendStatus" active-color="#13ce66" inactive-color="#ff4949"
  71. :active-value="1" :inactive-value="0" active-text="开启" inactive-text="关闭" @change="toggleSendStatus($event, scope.row)">
  72. </el-switch>
  73. </template>
  74. </el-table-column>
  75. <el-table-column label="创建时间" align="center" prop="createTime" min-width="180" />
  76. <el-table-column label="更新时间" align="center" prop="updateTime" min-width="180" />
  77. <el-table-column label="操作" align="center" class-name="small-padding fixed-width" min-width="180"
  78. fixed="right">
  79. <template slot-scope="scope">
  80. <el-button
  81. size="mini"
  82. type="text"
  83. icon="el-icon-edit"
  84. @click="handleUpdate(scope.row)"
  85. v-hasPermi="['app:welcome:edit']"
  86. >
  87. 修改
  88. </el-button>
  89. <el-button
  90. size="mini"
  91. type="text"
  92. icon="el-icon-delete"
  93. @click="handleDelete(scope.row)"
  94. v-hasPermi="['app:welcome:delete']"
  95. >
  96. 删除
  97. </el-button>
  98. </template>
  99. </el-table-column>
  100. </el-table>
  101. <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
  102. @pagination="getList" />
  103. <!-- 添加或修改好友欢迎语对话框 -->
  104. <el-dialog :title="addForm.title" :visible.sync="addForm.visible" v-loading="loading" width="1700px" append-to-body :close-on-click-modal="false">
  105. <div style="width: 1130px" class="app-container">
  106. <div>
  107. <span style="font-size: 15px">基础信息</span>
  108. <el-divider></el-divider>
  109. <el-alert title="同一个客服,如果绑定了多个欢迎语,则以最新绑定的那一条为准,本次保存涉及的客服,那么当前的欢迎语就是最新绑定的!!" type="info" :closable="false" show-icon>
  110. </el-alert>
  111. </div>
  112. <el-form ref="form" :model="form" :rules="rules" label-width="120px">
  113. <el-form-item label="选择使用客服:" prop="customerRoles" style="margin-top: 2%">
  114. <div>
  115. <el-button size="medium" icon="el-icon-circle-plus-outline" plain
  116. @click="openCustomerPage(1)">请选择使用客服</el-button>
  117. </div>
  118. <div>
  119. <el-tag style="margin-left: 5px" size="medium" :key="s.id" v-for="s in appCustomerBindConfig.selected" closable
  120. :disable-transitions="false" @close="handleCloseCustomer(s, 1)">
  121. <span>{{ s.memberName }}</span>
  122. </el-tag>
  123. </div>
  124. </el-form-item>
  125. <el-form-item label="发送状态" prop="sendStatus">
  126. <el-switch v-model="form.sendStatus" active-color="#13ce66" inactive-color="#ff4949"
  127. :active-value="1" :inactive-value="0" active-text="开启" inactive-text="关闭">
  128. </el-switch>
  129. </el-form-item>
  130. <div>
  131. <span style="font-size: 15px">欢迎语</span>
  132. <el-divider></el-divider>
  133. </div>
  134. <el-form-item label="默认欢迎语:" prop="welcomeText">
  135. <el-input v-model="form.welcomeText" type="textarea" :rows="12" maxlength="1300" show-word-limit
  136. placeholder="请输入消息内容" />
  137. <!-- 附件和链接列表 -->
  138. <el-row>
  139. <el-col>
  140. <div v-for="(item, index) in form.attachments" :key="index"
  141. style="background-color: #f5f7fa;padding: 5px;border: 1px solid #d9d9d9;">
  142. <div slot="header"
  143. style="display: flex;justify-content: space-between;align-items: center;">
  144. <div style="flex: 1;">
  145. <span v-if="item.contentType == '2'">【图片】: {{ item.image.imgUrl }}</span>
  146. <span v-else-if="item.contentType == '9'">【科普】: {{ item.course.courseTitle }}</span>
  147. <span v-else-if="item.contentType == '20'">【疗法】: {{ item.pkg.packageName }}</span>
  148. <span v-else-if="item.contentType == '21'">【直播】: {{ item.live.liveTitle }}</span>
  149. </div>
  150. <div style="display: flex;gap: 10px;">
  151. <el-button size="mini" type="text" icon="el-icon-edit" style="float: left;"
  152. @click="editFileItem(item, index)">修改</el-button>
  153. <el-button size="mini" type="text" icon="el-icon-delete"
  154. style="float: right;"
  155. @click="removeFileItem(index)">删除</el-button>
  156. </div>
  157. </div>
  158. </div>
  159. </el-col>
  160. </el-row>
  161. <el-dropdown @command="(command) => handleCommand(command)" trigger="click"
  162. placement="top-start">
  163. <el-dropdown-menu slot="dropdown" style="width: 120px;">
  164. <!-- 和模板那边的消息类型一致,文本在上面输入框输入,故此不显示在此处 -->
  165. <el-dropdown-item
  166. v-for="ct in contentTypeOptions"
  167. :key="ct.dictValue"
  168. :command="ct.dictValue"
  169. v-show="ct.dictValue != 11"
  170. >
  171. <i class="el-icon-link" style="margin-right: 10px;"></i>{{ ct.dictLabel }}
  172. </el-dropdown-item>
  173. </el-dropdown-menu>
  174. <span class="el-dropdown-link">
  175. <el-link icon="el-icon-paperclip" type="text" style="color: rgb(24, 144, 255)">
  176. 添加附件(最多9个)
  177. </el-link>
  178. </span>
  179. </el-dropdown>
  180. </el-form-item>
  181. </el-form>
  182. </div>
  183. <div slot="footer" class="dialog-footer" style="text-align: center">
  184. <el-button type="primary" @click="submitForm">确定</el-button>
  185. <el-button @click="cancel">取 消</el-button>
  186. </div>
  187. </el-dialog>
  188. <!-- 列表搜索条件:选择app客服角色 -->
  189. <el-dialog title="选择客服" :visible.sync="appCustomerSearchConfig.visible" width="1000px" append-to-body :close-on-click-modal="false">
  190. <CusRoleList
  191. ref="customerSearchRef"
  192. @confirm="customerSelectedConfirmCallForSearch"
  193. @selectionChange="customerSelectionChangeCallForSearch"
  194. :selected="appCustomerSearchConfig.selected"
  195. />
  196. </el-dialog>
  197. <!-- 新增编辑:选择app客服角色 -->
  198. <el-dialog title="选择客服" :visible.sync="appCustomerBindConfig.visible" width="1000px" append-to-body :close-on-click-modal="false">
  199. <CusRoleList
  200. ref="customerBindRef"
  201. @confirm="customerSelectedConfirmCallForBind"
  202. @selectionChange="customerSelectionChangeCallForBind"
  203. :selected="appCustomerBindConfig.selected"
  204. />
  205. </el-dialog>
  206. <!-- 新增/编辑页面内,欢迎语类型 -->
  207. <el-dialog :title="welcomeItem.title" :visible.sync="welcomeItem.visible" style="width: 1300px;height: 100%; margin-top: 10vh;"
  208. append-to-body :close-on-click-modal="false">
  209. <!-- 科普 -->
  210. <el-form v-if="welcomeItem.contentType == '9'" ref="courseForm" :model="courseForm" :rules="courseFormRule" label-width="110px">
  211. <div>
  212. <el-form-item label="选择科普" prop="videoId">
  213. <el-select
  214. v-model="courseForm.courseId"
  215. placeholder="默认50条,可输入关键字检索"
  216. style="margin-right: 10px;"
  217. size="mini"
  218. filterable
  219. :loading="welcomeItem.courseLoading"
  220. @change="courseChange(courseForm)"
  221. remote
  222. :remote-method="(keyword) => handleCourseRemoteSearch(keyword, courseForm)"
  223. >
  224. <el-option
  225. v-for="dict in courseList"
  226. :key="dict.dictValue"
  227. :label="dict.dictLabel"
  228. :value="parseInt(dict.dictValue)"
  229. />
  230. </el-select>
  231. <el-select
  232. v-model="courseForm.videoId"
  233. size="mini"
  234. placeholder="请选择小节"
  235. style="margin-right: 10px;"
  236. @change="videoIdChange(courseForm, welcomeItem.contentType)"
  237. >
  238. <el-option
  239. v-for="dict in videoList"
  240. :key="dict.dictValue"
  241. :label="dict.dictLabel"
  242. :value="parseInt(dict.dictValue)"
  243. >
  244. <div :style="{ color: dict.isPause == 1 ? '#f56c6c' : 'inherit', display: 'flex', alignItems: 'center' }">
  245. <span>{{ dict.dictLabel }}</span>
  246. <span v-if="dict.isPause == 1" style="font-size: 12px; margin-left: 8px; color: #f56c6c;">
  247. (该科普已被关闭,无法正常发送)
  248. </span>
  249. </div>
  250. </el-option>
  251. </el-select>
  252. </el-form-item>
  253. <el-form-item label="科普标题:" prop="courseTitle">
  254. <el-input
  255. v-model="courseForm.courseTitle"
  256. :rows="2"
  257. maxlength="64"
  258. placeholder="请输入科普标题,最长为64字节"
  259. @input="checkByteLength(courseForm, 'courseTitle')"
  260. />
  261. </el-form-item>
  262. <div v-if="courseForm.videoId != null ">
  263. <el-form-item label="课节链接:" label-width="100px">
  264. <el-tag type="warning">选择的科普小节 即为卡片链接地址</el-tag>
  265. </el-form-item>
  266. </div>
  267. <div v-if="courseForm.videoId != null">
  268. <el-form-item label="课节过期时间" style="margin-top: 1%" required label-width="110px">
  269. <el-row>
  270. <el-input-number v-model="courseForm.expireDays" :min="1" :max="9999"></el-input-number>
  271. (天)
  272. </el-row>
  273. <el-row>
  274. <span class="tip">默认为30天</span>
  275. </el-row>
  276. </el-form-item>
  277. </div>
  278. </div>
  279. </el-form>
  280. <!-- 图片 -->
  281. <el-form v-else-if="welcomeItem.contentType == '2'" ref="imageForm" :model="imageForm" :rules="imageFormRule" label-width="110px">
  282. <div>
  283. <el-form-item label="图片:" prop="imgUrl">
  284. <ImageUpload
  285. v-model="imageForm.imgUrl"
  286. type="image"
  287. :num="10"
  288. :width="150"
  289. :height="150"
  290. />
  291. </el-form-item>
  292. </div>
  293. </el-form>
  294. <!--语音: welcomeItem.contentType == '12' -->
  295. <!--视频: welcomeItem.contentType == '6' -->
  296. <!-- 疗法: welcomeItem.contentType == '20' -->
  297. <el-form v-else-if="welcomeItem.contentType == '20'" ref="pkgForm" :model="pkgForm" :rules="pkgFormRule" label-width="110px">
  298. <div>
  299. <el-form-item
  300. label="选择疗法:"
  301. label-width="100px"
  302. required
  303. >
  304. <el-select
  305. v-model="pkgForm.packageId"
  306. placeholder="默认50条,可输入关键字检索"
  307. style=" margin-right: 10px; width: 230px;"
  308. size="mini"
  309. filterable
  310. clearable
  311. :loading="pkgForm.pkgLoading"
  312. remote
  313. :remote-method="(keyword) => handlePkgRemoteSearch(keyword)"
  314. @change="pkgChangeUpdate"
  315. >
  316. <el-option
  317. v-for="dict in pkgList"
  318. :key="dict.packageId"
  319. :label="`${dict.packageName}(${dict.packageId})`"
  320. :value="parseInt(dict.packageId)"
  321. />
  322. </el-select>
  323. </el-form-item>
  324. <el-form-item
  325. label="疗法名称:"
  326. label-width="100px"
  327. >
  328. <el-input
  329. disabled
  330. :rows="3"
  331. v-model="pkgForm.packageName"
  332. style="width: 90%;margin-top: 1%;"
  333. />
  334. </el-form-item>
  335. <el-form-item
  336. label="疗法封面:"
  337. label-width="100px"
  338. >
  339. <ImageUpload
  340. :disabled="true"
  341. v-model="pkgForm.packageImgUrl"
  342. type="image"
  343. :num="1"
  344. :file-size="1"
  345. :width="150"
  346. :height="150"
  347. style="margin-top: 1%;"
  348. />
  349. </el-form-item>
  350. </div>
  351. </el-form>
  352. <!-- 直播 -->
  353. <!-- <el-form v-else-if="welcomeItem.contentType == '21'" ref="liveForm" :model="liveForm" :rules="liveFormRule" label-width="110px">-->
  354. <!-- <div>-->
  355. <!-- <el-card class="box-card">-->
  356. <!-- <el-form-item label="直播间"-->
  357. <!-- required-->
  358. <!-- style="margin-bottom: 5px;"-->
  359. <!-- >-->
  360. <!-- <el-select-->
  361. <!-- v-model="liveForm.liveId"-->
  362. <!-- placeholder="请选择直播间"-->
  363. <!-- size="mini"-->
  364. <!-- filterable-->
  365. <!-- @change="liveChange()"-->
  366. <!-- >-->
  367. <!-- <el-option-->
  368. <!-- v-for="dict in liveList"-->
  369. <!-- :key="dict.liveId"-->
  370. <!-- :label="dict.liveName"-->
  371. <!-- :value="parseInt(dict.liveId)"-->
  372. <!-- />-->
  373. <!-- </el-select>-->
  374. <!-- </el-form-item>-->
  375. <!-- <el-form-item label="标题"-->
  376. <!-- prop="liveTitle"-->
  377. <!-- style="margin-bottom: 5px;"-->
  378. <!-- >-->
  379. <!-- <el-input-->
  380. <!-- v-model="liveForm.liveTitle"-->
  381. <!-- placeholder="请输入消息标题,最长为64字节"-->
  382. <!-- :rows="2"-->
  383. <!-- maxlength="64"-->
  384. <!-- type="textarea"-->
  385. <!-- disabled-->
  386. <!-- @input="checkByteLength(liveForm, 'liveTitle')"-->
  387. <!-- />-->
  388. <!-- </el-form-item>-->
  389. <!-- <el-form-item label="封面"-->
  390. <!-- prop="liveImgUrl"-->
  391. <!-- >-->
  392. <!-- <ImageUpload-->
  393. <!-- v-model="liveForm.liveImgUrl"-->
  394. <!-- type="image"-->
  395. <!-- :num="10"-->
  396. <!-- :width="150"-->
  397. <!-- :height="150"-->
  398. <!-- disabled-->
  399. <!-- />-->
  400. <!-- </el-form-item>-->
  401. <!-- </el-card>-->
  402. <!-- </div>-->
  403. <!-- </el-form>-->
  404. <div slot="footer" class="dialog-footer" style="text-align: center">
  405. <el-button type="primary" @click="confirmUpload">确定</el-button>
  406. <el-button @click="cancelUpload">取消</el-button>
  407. </div>
  408. </el-dialog>
  409. </div>
  410. </template>
  411. <script>
  412. import { list, getById, deleteByIds, save, setSendStatus } from "@/api/app/welcome";
  413. import {listRole} from '@/api/app/user/userList';
  414. import ImageUpload from "@/views/qw/sop/ImageUpload";
  415. import CusRoleList from '@/views/app/user/CusRoleList.vue';
  416. import {courseList, videoList} from "@/api/app/course";
  417. import { getPackageOptions,} from "@/api/app/pkg";
  418. // import { getLiveOptions,} from "@/api/app/live";
  419. export default {
  420. name: "AppWelcome",
  421. components: { ImageUpload, CusRoleList },
  422. data() {
  423. return {
  424. loading: true,
  425. exportLoading: false,
  426. ids: [],
  427. appCustomerSearchConfig: {
  428. type: null,
  429. visible: false,
  430. enableFilter: false,
  431. selected: [],
  432. },
  433. appCustomerBindConfig: {
  434. type: null,
  435. visible: false,
  436. enableFilter: false,
  437. selected: [],
  438. },
  439. addForm: {
  440. visible: false,
  441. title: "",
  442. },
  443. contentTypeOptions: [],
  444. courseForm: {
  445. courseId: null,//科普-科普id
  446. videoId: null,//科普-科普小结id
  447. expireDays: 30,//科普-科普小节过期时间(天)
  448. courseTitle: null,//科普-科普小结标题
  449. courseLoading: false,
  450. },
  451. courseFormRule: {
  452. courseId: [
  453. { required: true, message: "请选择科普信息", trigger: "blur" }
  454. ],
  455. videoId: [
  456. { required: true, message: "请选择科普信息", trigger: "blur" }
  457. ],
  458. courseTitle: [
  459. { required: true, message: "请输入科普标题", trigger: "blur" }
  460. ]
  461. },
  462. imageForm: {
  463. imgUrl: null,//图片url
  464. },
  465. pkgList: [],
  466. pkgForm: {
  467. packageId: null,
  468. packageName: null,
  469. packageImgUrl: null,
  470. loading: false,
  471. },
  472. pkgFormRule: {},
  473. imageFormRule: {},
  474. liveList: [],
  475. liveForm: {
  476. liveId: null,
  477. liveTitle: null,
  478. liveImgUrl: null,
  479. },
  480. liveFormRule: {},
  481. courseList: [],
  482. videoList: [],
  483. welcomeItem: {
  484. visible: false,//控制添加附件对话框是否显示
  485. title: null,//对话框标题
  486. contentType: null,//附件类型
  487. index: -1,//新增,还是编辑,-1即为新增,>-1即为编辑
  488. },
  489. showSearch: true,
  490. total: 0,
  491. welcomeList: [],
  492. queryParams: {
  493. pageNum: 1,
  494. pageSize: 10,
  495. customerRoles: [],
  496. createTime: null,
  497. sendStatus: null,
  498. },
  499. form: {},
  500. rules: {
  501. welcomeText: [
  502. { required: true, message: "消息内容不能为空", trigger: "submit" }
  503. ],
  504. },
  505. };
  506. },
  507. async created() {
  508. let defaultCourse = await this.getCourseList(null, null);
  509. this.courseList = defaultCourse.list;
  510. let contentTypeOptions = await this.getDicts("app_sop_plugin_settingType");
  511. this.contentTypeOptions = contentTypeOptions.data;
  512. this.getList();
  513. let defaultPkg = await this.getPkgList(null, null);
  514. this.pkgList = defaultPkg.data;
  515. // let defaultLive = await this.getLiveList();
  516. // this.liveList = defaultLive.data;
  517. },
  518. methods: {
  519. /**
  520. * 获取科普信息
  521. */
  522. async getCourseList(keyword, selectedId) {
  523. let data = {
  524. keyword: keyword,
  525. selectedId: selectedId,
  526. pageNum: 1,
  527. pageSize: 50,
  528. };
  529. return await courseList(data);
  530. },
  531. /**
  532. * 获取疗信息
  533. */
  534. async getPkgList(keyword, selectedId) {
  535. let data = {
  536. keyword: keyword,
  537. metaId: selectedId,
  538. limit: 50
  539. };
  540. return await getPackageOptions(data);
  541. },
  542. /**
  543. * 获取直播间信息
  544. */
  545. // async getLiveList() {
  546. // return await getLiveOptions();
  547. // },
  548. /**
  549. * 疗法下拉搜索
  550. * @param keyword
  551. */
  552. async handlePkgRemoteSearch(keyword) {
  553. this.pkgForm.pkgLoading = true;
  554. // 模拟远程接口请求(替换为你的真实接口)
  555. let cl = await this.getPkgList(keyword ? null : keyword, null);
  556. this.pkgForm.pkgLoading = false;
  557. this.pkgList = cl.data;
  558. },
  559. /**
  560. * 疗法变动处理
  561. */
  562. async pkgChangeUpdate() {
  563. this.pkgForm.packageName = null;
  564. this.pkgForm.packageImgUrl = null;
  565. if (this.pkgForm.packageId) {
  566. // 查找选中疗法的信息
  567. const selectedPkg = this.pkgList.find(pkg => parseInt(pkg.packageId) === this.pkgForm.packageId);
  568. this.pkgForm.packageName = selectedPkg.packageName;
  569. this.pkgForm.packageImgUrl = selectedPkg.imgUrl;
  570. }
  571. },
  572. // /**
  573. // * 直播间切换
  574. // */
  575. // liveChange() {
  576. // this.liveForm.liveTitle = null;
  577. // this.liveForm.liveImgUrl = null;
  578. // const selectedLive = this.liveList.find(live => live.liveId === this.liveForm.liveId);
  579. // if (selectedLive) {
  580. // this.liveForm.liveTitle = selectedLive.liveName; // 自动填充标题
  581. // this.liveForm.liveImgUrl = selectedLive.liveImgUrl; // 自动填充封面
  582. // }
  583. // },
  584. /**
  585. * 搜索条件:客服选中确认回调
  586. */
  587. customerSelectedConfirmCallForSearch() {
  588. this.appCustomerSearchConfig.visible = false;
  589. },
  590. /**
  591. * 新增/编辑:客服选中确认回调
  592. */
  593. customerSelectedConfirmCallForBind() {
  594. this.appCustomerBindConfig.visible = false;
  595. },
  596. /**
  597. * 搜索条件:客服选中变更回调
  598. * @param config
  599. */
  600. customerSelectionChangeCallForSearch(config) {
  601. let selected = config.selected;
  602. //单行操作
  603. if (config.mode === 0) {
  604. let row = config.row;
  605. if (selected) {
  606. if (!this.appCustomerSearchConfig.selected.some(s => s.id == row.id)) {
  607. this.appCustomerSearchConfig.selected.push(row);
  608. }
  609. } else {
  610. this.appCustomerSearchConfig.selected = this.appCustomerSearchConfig.selected.filter(m => m.id != row.id);
  611. }
  612. }
  613. //操作当前页所有行
  614. else {
  615. let list = config.rows;
  616. if (selected) {
  617. list.map(l => {
  618. if (!this.appCustomerSearchConfig.selected.some(s => s.id == l.id)) {
  619. this.appCustomerSearchConfig.selected.push(l);
  620. }
  621. });
  622. } else {
  623. list.map(l => {
  624. for (let i = 0; i < this.appCustomerSearchConfig.selected.length; i++) {
  625. if (l.id == this.appCustomerSearchConfig.selected[i].id) {
  626. this.appCustomerSearchConfig.selected.splice(i, 1);
  627. }
  628. }
  629. })
  630. }
  631. }
  632. },
  633. /**
  634. * 新增/编辑:客服选中变更回调
  635. * @param config
  636. */
  637. customerSelectionChangeCallForBind(config) {
  638. let selected = config.selected;
  639. //单行操作
  640. if (config.mode === 0) {
  641. let row = config.row;
  642. if (selected) {
  643. if (!this.appCustomerBindConfig.selected.some(s => s.id == row.id)) {
  644. this.appCustomerBindConfig.selected.push(row)
  645. }
  646. } else {
  647. this.appCustomerBindConfig.selected = this.appCustomerBindConfig.selected.filter(m => m.id != row.id);
  648. }
  649. }
  650. //操作当前页所有行
  651. else {
  652. let list = config.rows;
  653. if (selected) {
  654. list.map(l => {
  655. if (!this.appCustomerBindConfig.selected.some(s => s.id == l.id)) {
  656. this.appCustomerBindConfig.selected.push(l)
  657. }
  658. });
  659. } else {
  660. list.map(l => {
  661. for (let i = 0; i < this.appCustomerBindConfig.selected.length; i++) {
  662. if (l.id == this.appCustomerBindConfig.selected[i].id) {
  663. this.appCustomerBindConfig.selected.splice(i, 1);
  664. }
  665. }
  666. })
  667. }
  668. }
  669. },
  670. /**
  671. * 打开选择客服页面
  672. * @param mode 0-搜索,1-新增/编辑
  673. */
  674. openCustomerPage(mode) {
  675. if (mode == 0) {
  676. this.appCustomerSearchConfig.visible = true;
  677. } else {
  678. this.appCustomerBindConfig.visible = true;
  679. }
  680. },
  681. /**
  682. * 删除客服
  683. * @param cus
  684. * @param mode
  685. */
  686. handleCloseCustomer(cus, mode) {
  687. if (mode == 0) {
  688. this.appCustomerSearchConfig.selected = this.appCustomerSearchConfig.selected.filter(item => item.id != cus.id);
  689. } else {
  690. this.appCustomerBindConfig.selected = this.appCustomerBindConfig.selected.filter(item => item.id != cus.id);
  691. }
  692. },
  693. getList() {
  694. this.loading = true;
  695. list(this.queryParams).then(response => {
  696. this.welcomeList = response.rows;
  697. this.total = response.total;
  698. this.loading = false;
  699. });
  700. },
  701. /**
  702. * 检查长度
  703. * @param formInfo
  704. * @param key
  705. */
  706. checkByteLength(formInfo, key) {
  707. const text = formInfo[key];
  708. const byteLength = this.getByteLength(text);
  709. if (byteLength > 64) {
  710. this.$set(formInfo, key, this.truncateTextByByteLength(text, 64));
  711. }
  712. },
  713. /**
  714. * 获取字节长度
  715. * @param text
  716. */
  717. getByteLength(text) {
  718. return new Blob([text]).size;
  719. },
  720. /**
  721. * 截断字节长度
  722. * @param text
  723. * @param maxByteLength
  724. */
  725. truncateTextByByteLength(text, maxByteLength) {
  726. let byteLength = 0;
  727. let result = "";
  728. for (let i = 0; i < text.length; i++) {
  729. const char = text[i];
  730. const charByteLength = this.getByteLength(char);
  731. if (byteLength + charByteLength <= maxByteLength) {
  732. result += char;
  733. byteLength += charByteLength;
  734. } else {
  735. break;
  736. }
  737. }
  738. return result;
  739. },
  740. /**
  741. * 点击具体的欢迎语选项进行新增附件
  742. * @param command
  743. */
  744. handleCommand(command) {
  745. if (this.form.attachments.length >= 9) {
  746. return this.$message.error('附件数量已达上限,无法添加更多附件');
  747. }
  748. this.welcomeItem = {
  749. visible: true,
  750. title: '添加' + this.getTitleByCommand(command),
  751. contentType: command,
  752. index: -1,
  753. };
  754. },
  755. /**
  756. * 根据消息类型编码获取消息类型名称
  757. * @param command
  758. */
  759. getTitleByCommand(command) {
  760. let option = this.contentTypeOptions.find(cto => cto.dictValue == command);
  761. return option.dictLabel;
  762. },
  763. /**
  764. *
  765. */
  766. async handleCourseRemoteSearch(keyword, courseForm) {
  767. this.welcomeItem.courseLoading = true;
  768. // 模拟远程接口请求(替换为你的真实接口)
  769. let cl = await this.getCourseList((keyword ? keyword : null), courseForm.courseId);
  770. this.courseList = cl.list;
  771. this.welcomeItem.courseLoading = false;
  772. },
  773. /**
  774. * 科普变化
  775. * @param form
  776. */
  777. courseChange(form) {
  778. this.$set(form, 'videoId', null);
  779. this.$set(form, 'courseTitle', null);
  780. this.videoList = [];
  781. if (form.courseId != null) {
  782. videoList({courseId: form.courseId}).then(response => {
  783. this.videoList = response.list;
  784. });
  785. }
  786. },
  787. /**
  788. * 科普小节改变
  789. * @param form
  790. * @param type
  791. */
  792. videoIdChange(form, type) {
  793. if (form.videoId != null) {
  794. let selectedVideo = this.videoList.find(course => course.dictValue == form.videoId);
  795. //科普
  796. if (selectedVideo && type == '9') {
  797. this.$set(form, 'courseTitle', this.truncateTextByByteLength(selectedVideo.dictLabel, 64));
  798. this.$set(form, 'expireDays', 30);
  799. }
  800. }
  801. },
  802. /**
  803. * 编辑附件信息
  804. * @param item
  805. */
  806. async editFileItem(item, index) {
  807. this.welcomeItem = {
  808. visible: true,
  809. title: '编辑' + this.getTitleByCommand(item.contentType),
  810. contentType: item.contentType,
  811. index,
  812. };
  813. //图片
  814. if (item.contentType == '2') {
  815. this.imageForm = JSON.parse(JSON.stringify(item.image));//深拷贝
  816. }
  817. // 科普
  818. else if (item.contentType == '9') {
  819. this.courseForm = JSON.parse(JSON.stringify(item.course));//深拷贝
  820. if (item.course.courseId) {
  821. let cl = await this.getCourseList(null, this.courseForm.courseId);
  822. this.courseList = cl.list;
  823. videoList({courseId: item.course.courseId}).then(response => {
  824. this.videoList = response.list;
  825. });
  826. }
  827. }
  828. },
  829. /**
  830. * 移除欢迎语附件
  831. * @param index
  832. */
  833. removeFileItem(index) {
  834. this.form.attachments.splice(index, 1);
  835. },
  836. /**
  837. * 确认添加附件
  838. */
  839. confirmUpload() {
  840. const { contentType, index } = this.welcomeItem;
  841. //科普
  842. if (contentType == '9') {
  843. this.$refs["courseForm"].validate(valid => {
  844. if (valid) {
  845. this.addFile(index, {
  846. contentType: 9,
  847. course: {...this.courseForm},
  848. });
  849. }
  850. });
  851. }
  852. //图片
  853. else if (contentType == '2') {
  854. this.$refs["imageForm"].validate(valid => {
  855. if (valid) {
  856. this.addFile(index, {
  857. contentType: 2,
  858. image: {...this.imageForm},
  859. });
  860. }
  861. });
  862. }
  863. //疗法
  864. else if (contentType == '20') {
  865. this.$refs["pkgForm"].validate(valid => {
  866. if (valid) {
  867. this.addFile(index, {
  868. contentType: 20,
  869. pkg: {...this.pkgForm},
  870. });
  871. }
  872. });
  873. }
  874. //直播
  875. else if (contentType == '21') {
  876. this.$refs["liveForm"].validate(valid => {
  877. if (valid) {
  878. this.addFile(index, {
  879. contentType: 21,
  880. live: {...this.liveForm},
  881. });
  882. }
  883. });
  884. }
  885. },
  886. /**
  887. * 添加附件
  888. * @param index
  889. * @param attachment
  890. */
  891. addFile(index, attachment) {
  892. //编辑模式:执行替换
  893. if (index > -1) {
  894. this.form.attachments.splice(index, 1, attachment);
  895. }
  896. //新增模式:直接追加
  897. else {
  898. this.form.attachments.push(attachment);
  899. }
  900. this.resetFileForm();
  901. this.welcomeItem.visible = false;
  902. },
  903. /**
  904. * 取消添加附件
  905. */
  906. cancelUpload() {
  907. this.resetFileForm();
  908. this.welcomeItem.visible = false;
  909. },
  910. /**
  911. * 取消新增欢迎语
  912. */
  913. cancel() {
  914. this.addForm.visible = false;
  915. this.reset();
  916. },
  917. /**
  918. * 重置附件表单
  919. */
  920. resetFileForm() {
  921. //清空科普表单信息
  922. for (let key in this.courseForm) {
  923. this.courseForm[key] = null;
  924. }
  925. //清空图片表单信息
  926. for (let key in this.imageForm) {
  927. this.imageForm[key] = null;
  928. }
  929. this.welcomeItem = {
  930. visible: false,
  931. title: null,
  932. contentType: null,
  933. index: -1,
  934. };
  935. this.videoList = [];
  936. },
  937. /**
  938. * 切换发送状态
  939. * @param val
  940. */
  941. async toggleSendStatus(val, row) {
  942. console.log(row)
  943. let activeName = val == 0 ? '关闭' : '开启';
  944. this.$confirm(`是否确认${activeName}发送?`, "警告", {
  945. confirmButtonText: "确定",
  946. cancelButtonText: "取消",
  947. type: "warning"
  948. }).then(async res => {
  949. await setSendStatus({id: row.id, sendStatus: val});
  950. this.msgSuccess(`${activeName}成功!`);
  951. this.getList();
  952. }).catch(() => {
  953. this.getList();
  954. });
  955. },
  956. /**
  957. * 重置新增欢迎语表单
  958. */
  959. reset() {
  960. this.form = {
  961. id: null,
  962. attachments: [],
  963. sendStatus: 1,
  964. welcomeText: null,
  965. };
  966. this.appCustomerBindConfig.selected = [];
  967. this.resetForm("form");
  968. this.resetFileForm();
  969. },
  970. /**
  971. * 执行查询
  972. */
  973. handleQuery() {
  974. this.queryParams.pageNum = 1;
  975. this.queryParams.customerRoles = this.appCustomerSearchConfig.selected.map(s => s.id) || null;
  976. this.getList();
  977. },
  978. /**
  979. * 重置查询
  980. */
  981. resetQuery() {
  982. this.resetForm("queryForm");
  983. this.appCustomerSearchConfig.selected = [];
  984. this.handleQuery();
  985. },
  986. /**
  987. * 新增欢迎语
  988. */
  989. handleAdd() {
  990. this.reset();
  991. this.addForm.visible = true;
  992. this.addForm.title = "新建欢迎语";
  993. },
  994. /**
  995. * 编辑欢迎语
  996. * @param row
  997. */
  998. async handleUpdate(row) {
  999. this.reset();
  1000. const id = row.id;
  1001. let info = await getById(id);
  1002. let data = info.data;
  1003. if (data.attachments) {
  1004. data.attachments = JSON.parse(data.attachments);
  1005. }
  1006. if (data.customerRoles) {
  1007. let roleList = await listRole({ids: data.customerRoles.split(',')});
  1008. this.appCustomerBindConfig.selected = roleList.rows;
  1009. }
  1010. this.form = data;
  1011. this.addForm.visible = true;
  1012. this.addForm.title = "修改欢迎语";
  1013. },
  1014. /**
  1015. * 提交欢迎语表单
  1016. */
  1017. submitForm() {
  1018. this.loading = true;
  1019. this.$refs["form"].validate(valid => {
  1020. if (valid) {
  1021. this.commitForm();
  1022. }
  1023. });
  1024. },
  1025. /**
  1026. * 提交表单
  1027. */
  1028. async commitForm() {
  1029. const requestData = { ...this.form };
  1030. requestData['customerRoles'] = this.appCustomerBindConfig.selected.map(s => s.id);
  1031. requestData.attachments = JSON.stringify(this.form.attachments);
  1032. await save(requestData);
  1033. this.reset();
  1034. this.addForm.visible = false;
  1035. this.getList();
  1036. },
  1037. /**
  1038. * 删除
  1039. * @param row
  1040. */
  1041. handleDelete(row) {
  1042. const ids = row.id || this.ids;
  1043. this.$confirm('是否确认删除欢迎语:编号为"' + ids + '"的数据项?', "警告", {
  1044. confirmButtonText: "确定",
  1045. cancelButtonText: "取消",
  1046. type: "warning"
  1047. }).then(function () {
  1048. return deleteByIds(ids);
  1049. }).then(() => {
  1050. this.getList();
  1051. this.msgSuccess("删除成功");
  1052. }).catch(() => { });
  1053. },
  1054. }
  1055. };
  1056. </script>
  1057. <style>
  1058. .text-container {
  1059. max-height: 7.5em;
  1060. overflow-y: auto;
  1061. line-height: 1.5em;
  1062. }
  1063. </style>