index.vue 52 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206
  1. <template>
  2. <div class="app-container">
  3. <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
  4. <el-form-item label="模板名称" prop="name">
  5. <el-input v-model="queryParams.name" placeholder="请输入模板名称" clearable size="small"
  6. @keyup.enter.native="handleQuery" />
  7. </el-form-item>
  8. <el-form-item label="状态" prop="status">
  9. <el-select v-model="queryParams.status" placeholder="请选择状态" clearable size="small">
  10. <el-option v-for="dict in statusOptions" :key="dict.dictValue" :label="dict.dictLabel"
  11. :value="dict.dictValue" />
  12. </el-select>
  13. </el-form-item>
  14. <el-form-item label="科普" prop="courseId">
  15. <el-select
  16. v-model="queryParams.courseId"
  17. placeholder="默认50条,可输入关键字检索"
  18. style=" margin-right: 10px;"
  19. size="mini"
  20. filterable
  21. clearable
  22. :loading="courseLoading"
  23. remote
  24. :remote-method="(keyword) => handleCourseRemoteSearch(keyword, 0)"
  25. @change="courseChangeUpdate(0)"
  26. @keyup.enter.native="handleQuery"
  27. >
  28. <el-option
  29. v-for="dict in queryCourseList"
  30. :key="dict.dictValue"
  31. :label="dict.dictLabel"
  32. :value="parseInt(dict.dictValue)"
  33. />
  34. </el-select>
  35. </el-form-item>
  36. <el-form-item label="小节" prop="videoId">
  37. <el-select
  38. v-model="queryParams.videoId"
  39. placeholder="请选择小节"
  40. size="mini"
  41. style=" margin-right: 10px;"
  42. filterable
  43. clearable
  44. :disabled="!queryParams.courseId"
  45. @change="videoIdChange(0)"
  46. @keyup.enter.native="handleQuery"
  47. >
  48. <el-option
  49. v-for="dict in queryVideoList"
  50. :key="dict.dictValue"
  51. :label="dict.dictLabel"
  52. :value="parseInt(dict.dictValue)"
  53. >
  54. <div :style="{ color: dict.isPause == 1 ? '#f56c6c' : 'inherit', display: 'flex', alignItems: 'center' }">
  55. <span>{{ dict.dictLabel }}</span>
  56. <span v-if="dict.isPause == 1" style="font-size: 12px; margin-left: 8px; color: #f56c6c;">
  57. (该科普已被关闭)
  58. </span>
  59. </div>
  60. </el-option>
  61. </el-select>
  62. </el-form-item>
  63. <el-form-item>
  64. <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
  65. <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
  66. </el-form-item>
  67. </el-form>
  68. <el-row :gutter="10" class="mb8">
  69. <el-col :span="1.5">
  70. <el-button
  71. type="primary"
  72. plain
  73. icon="el-icon-plus"
  74. size="mini"
  75. @click="handleAdd"
  76. v-hasPermi="['app:finishTemp:add']"
  77. >
  78. 新增
  79. </el-button>
  80. </el-col>
  81. <el-col :span="1.5">
  82. <el-button
  83. type="danger"
  84. plain
  85. icon="el-icon-delete"
  86. size="mini"
  87. @click="handleDelete"
  88. :disabled="multiple"
  89. v-hasPermi="['app:finishTemp:delete']"
  90. >
  91. 删除
  92. </el-button>
  93. </el-col>
  94. <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
  95. </el-row>
  96. <el-table border v-loading="loading" :data="courseFinishTempList" @selection-change="handleSelectionChange">
  97. <el-table-column type="selection" width="55" align="center" />
  98. <el-table-column label="模板ID" align="center" prop="id" />
  99. <el-table-column label="模板名称" align="center" prop="name" />
  100. <el-table-column label="状态" align="center" prop="status">
  101. <template slot-scope="scope">
  102. <dict-tag :options="statusOptions" :value="scope.row.status" />
  103. </template>
  104. </el-table-column>
  105. <el-table-column label="科普名称" align="center" prop="courseName" />
  106. <el-table-column label="小节名称" align="center" prop="videoName" />
  107. <el-table-column label="创建时间" align="center" prop="createTime" />
  108. <el-table-column label="修改时间" align="center" prop="updateTime" />
  109. <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
  110. <template slot-scope="scope">
  111. <el-button
  112. size="mini"
  113. type="text"
  114. icon="el-icon-edit"
  115. @click="handleUpdate(scope.row, true)"
  116. v-hasPermi="['app:finishTemp:edit']"
  117. >
  118. 修改
  119. </el-button>
  120. <el-button
  121. size="mini"
  122. type="text"
  123. icon="el-icon-s-promotion"
  124. @click="handleUpdate(scope.row, false)"
  125. >
  126. 详情
  127. </el-button>
  128. <el-button
  129. size="mini"
  130. type="text"
  131. icon="el-icon-delete"
  132. @click="handleDelete(scope.row)"
  133. v-hasPermi="['app:finishTemp:delete']"
  134. >
  135. 删除
  136. </el-button>
  137. </template>
  138. </el-table-column>
  139. </el-table>
  140. <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum"
  141. :limit.sync="queryParams.pageSize" @pagination="getList" />
  142. <!-- 添加或修改完课模板对话框 -->
  143. <el-dialog
  144. :title="addForm.title"
  145. :visible.sync="addForm.visible"
  146. width="1000px"
  147. v-loading="addForm.loading"
  148. append-to-body
  149. :close-on-click-modal="false"
  150. >
  151. <el-form ref="addForm" :model="addForm" :rules="rules" label-width="80px">
  152. <el-form-item label="模板名称" prop="name">
  153. <el-input v-model="addForm.name" :disabled="!addForm.canOp" placeholder="请输入模板名称" />
  154. </el-form-item>
  155. <el-form-item label="状态">
  156. <el-radio-group v-model="addForm.status">
  157. <el-radio v-for="dict in statusOptions"
  158. :key="dict.dictValue"
  159. :disabled="!addForm.canOp"
  160. :label="parseInt(dict.dictValue)"
  161. >
  162. {{ dict.dictLabel }}
  163. </el-radio>
  164. </el-radio-group>
  165. </el-form-item>
  166. <el-form-item label="科普"
  167. style="margin-bottom: 15px;"
  168. prop="videoId"
  169. >
  170. <el-select
  171. v-model="addForm.courseId"
  172. placeholder="默认50条,可输入关键字检索"
  173. style=" margin-right: 10px;"
  174. size="mini"
  175. filterable
  176. clearable
  177. :loading="courseLoading"
  178. remote
  179. :disabled="!addForm.canOp"
  180. :remote-method="(keyword) => handleCourseRemoteSearch(keyword, 1)"
  181. @change="courseChangeUpdate(1)"
  182. >
  183. <el-option
  184. v-for="dict in courseList"
  185. :key="dict.dictValue"
  186. :label="dict.dictLabel"
  187. :value="parseInt(dict.dictValue)"
  188. />
  189. </el-select>
  190. <el-select
  191. v-model="addForm.videoId"
  192. placeholder="请选择小节"
  193. size="mini"
  194. style=" margin-right: 10px;"
  195. filterable
  196. clearable
  197. :disabled="!addForm.canOp"
  198. @change="videoIdChange(1)"
  199. >
  200. <el-option
  201. v-for="dict in videoList"
  202. :key="dict.dictValue"
  203. :label="dict.dictLabel"
  204. :value="parseInt(dict.dictValue)"
  205. >
  206. <div :style="{ color: dict.isPause == 1 ? '#f56c6c' : 'inherit', display: 'flex', alignItems: 'center' }">
  207. <span>{{ dict.dictLabel }}</span>
  208. <span v-if="dict.isPause == 1" style="font-size: 12px; margin-left: 8px; color: #f56c6c;">
  209. (该科普已被关闭,无法正常发送)
  210. </span>
  211. </div>
  212. </el-option>
  213. </el-select>
  214. <!-- 默认完课消息 -->
  215. <el-select
  216. v-model="addForm.courseType"
  217. placeholder="请选择消息类型"
  218. size="mini"
  219. style=" margin-right: 10px;"
  220. disabled
  221. >
  222. <el-option
  223. v-for="dict in sysFsSopWatchStatus"
  224. :key="dict.dictValue"
  225. :label="dict.dictLabel"
  226. :value="Number(dict.dictValue)"
  227. />
  228. </el-select>
  229. </el-form-item>
  230. <el-form-item label="规则" style="height: 500px; overflow: auto;">
  231. <!-- 遍历规则 所有内容【存在多个】 -->
  232. <div
  233. v-for="(content, contentIndex) in addForm.setting"
  234. :key="contentIndex"
  235. style="
  236. background-color: #fdfdfd;
  237. border: 1px solid #e6e6e6;
  238. margin-bottom: 20px;
  239. "
  240. >
  241. <el-row style="padding-bottom: 20px">
  242. <el-col :span="22">
  243. <el-form
  244. :model="content"
  245. label-width="70px"
  246. >
  247. <el-form-item label="内容类别"
  248. style="margin: 2%"
  249. >
  250. <div>
  251. <el-radio-group
  252. v-model="content.contentType"
  253. @change="handleContentTypeChange()"
  254. >
  255. <el-radio
  256. :disabled="!addForm.canOp || !['11', '20'].includes(item.dictValue)"
  257. :key="item.dictValue"
  258. :label="item.dictValue"
  259. v-for="item in sysQwSopAiContentType"
  260. >{{ item.dictLabel }}
  261. </el-radio>
  262. </el-radio-group>
  263. </div>
  264. </el-form-item>
  265. <!-- 科普 -->
  266. <el-form-item label="内容" v-if="[9].includes(Number(content.contentType))">
  267. <div>
  268. <el-card class="box-card">
  269. <el-form-item
  270. label="链接标题:"
  271. label-width="100px"
  272. required
  273. >
  274. <el-input
  275. v-model="content.linkTitle"
  276. placeholder="请输入链接标题"
  277. style="width: 90%;"
  278. :disabled="!addForm.canOp"
  279. />
  280. </el-form-item>
  281. <el-form-item
  282. label="链接描述:"
  283. label-width="100px"
  284. required
  285. >
  286. <el-input
  287. type="textarea"
  288. :rows="3"
  289. :disabled="!addForm.canOp"
  290. v-model="content.linkDescribe"
  291. placeholder="请输入链接描述"
  292. style="width: 90%;margin-top: 1%;"
  293. />
  294. </el-form-item>
  295. <el-form-item
  296. label="链接封面:"
  297. label-width="100px"
  298. required
  299. >
  300. <ImageUpload
  301. v-model="content.linkImageUrl"
  302. type="image"
  303. :num="1"
  304. :file-size="1"
  305. :width="150"
  306. :height="150"
  307. style="margin-top: 1%;"
  308. />
  309. </el-form-item>
  310. <div>
  311. <el-form-item
  312. label="链接地址:"
  313. label-width="100px"
  314. >
  315. <el-tag
  316. type="warning"
  317. v-model="content.isBindUrl"
  318. >选择的科普小节
  319. 即为卡片链接地址
  320. </el-tag>
  321. </el-form-item>
  322. </div>
  323. <el-form-item
  324. label="课节过期时间"
  325. >
  326. <el-row>
  327. <el-input
  328. type="number"
  329. v-model="content.expiresDays"
  330. style="width: 200px"
  331. >
  332. <template slot="append">天</template>
  333. </el-input>
  334. </el-row>
  335. <el-row>
  336. <span class="tip">填写0或不填时,默认为系统配置的默认时间</span>
  337. </el-row>
  338. </el-form-item>
  339. </el-card>
  340. </div>
  341. </el-form-item>
  342. <!-- 文本 -->
  343. <el-form-item label="内容" v-else-if="[11].includes(Number(content.contentType))">
  344. <el-input
  345. v-model="content.value"
  346. type="textarea"
  347. :rows="3"
  348. placeholder="内容"
  349. :disabled="!addForm.canOp"
  350. style="width: 90%; margin-top: 10px;"
  351. @keydown.native="handleKeydown($event, contentIndex)"
  352. :ref="`textarea-${contentIndex}`"
  353. />
  354. </el-form-item>
  355. <!-- 语音 -->
  356. <el-form-item label="内容" v-else-if="[12].includes(Number(content.contentType))">
  357. <div >
  358. <el-input
  359. v-model="content.value"
  360. type="textarea"
  361. :rows="3"
  362. :disabled="!addForm.canOp"
  363. maxlength="66"
  364. show-word-limit
  365. placeholder="输入要转为语音的内容"
  366. style="width: 90%;margin-top: 10px;"
  367. @input="handleInputVoiceText(content.value, content)"
  368. />
  369. </div>
  370. </el-form-item>
  371. <!-- 图片 -->
  372. <el-form-item label="内容" v-else-if="[2].includes(Number(content.contentType))">
  373. <ImageUpload
  374. v-model="content.imgUrl"
  375. type="image"
  376. :num="1"
  377. :width="150"
  378. :height="150"
  379. />
  380. </el-form-item>
  381. <!-- 视频 -->
  382. <el-form-item label="内容" v-else-if="[6].includes(Number(content.contentType))">
  383. <div>
  384. <el-form-item
  385. label="上传视频:"
  386. prop="videoUrl"
  387. label-width="100px"
  388. >
  389. <el-upload
  390. v-model="content.videoUrl"
  391. class="avatar-uploader"
  392. :action="uploadUrl"
  393. :show-file-list="false"
  394. :disabled="!addForm.canOp"
  395. :on-success="(res, file) => handleSuccessVideo(res, file, content)"
  396. :before-upload="beforeUploadVideo"
  397. >
  398. <i class="el-icon-plus avatar-uploader-icon"></i>
  399. </el-upload>
  400. <video
  401. v-if="content.videoUrl"
  402. :src="content.videoUrl"
  403. controls
  404. style="width: 200px;height: 100px"
  405. >
  406. </video>
  407. </el-form-item>
  408. </div>
  409. </el-form-item>
  410. <!-- 疗法 -->
  411. <el-form-item label="内容" v-else-if="[20].includes(Number(content.contentType))">
  412. <el-card class="box-card">
  413. <el-form-item
  414. label="选择疗法:"
  415. label-width="100px"
  416. required
  417. >
  418. <el-select
  419. :disabled="!addForm.canOp"
  420. v-model="content.packageId"
  421. placeholder="默认50条,可输入关键字检索"
  422. style=" margin-right: 10px; width: 230px;"
  423. size="mini"
  424. filterable
  425. clearable
  426. :loading="content.pkgLoading"
  427. remote
  428. :remote-method="(keyword) => handlePkgRemoteSearch(keyword, content)"
  429. @change="pkgChangeUpdate(content)"
  430. >
  431. <el-option
  432. v-for="dict in (content.meta && content.meta.pkgList ? content.meta.pkgList : [])"
  433. :key="dict.packageId"
  434. :label="`${dict.packageName}(${dict.packageId})`"
  435. :value="parseInt(dict.packageId)"
  436. />
  437. </el-select>
  438. </el-form-item>
  439. <el-form-item
  440. label="疗法名称:"
  441. label-width="100px"
  442. >
  443. <el-input
  444. disabled
  445. :rows="3"
  446. v-model="content.packageName"
  447. style="width: 90%;margin-top: 1%;"
  448. />
  449. </el-form-item>
  450. <el-form-item
  451. label="疗法封面:"
  452. label-width="100px"
  453. >
  454. <ImageUpload
  455. :disabled="true"
  456. v-model="content.packageImgUrl"
  457. type="image"
  458. :num="1"
  459. :file-size="1"
  460. :width="150"
  461. :height="150"
  462. style="margin-top: 1%;"
  463. />
  464. </el-form-item>
  465. </el-card>
  466. </el-form-item>
  467. </el-form>
  468. </el-col>
  469. <el-col
  470. :span="1"
  471. :offset="1"
  472. >
  473. <!-- 删除内容节点 -->
  474. <i
  475. class="el-icon-delete"
  476. @click="delContentNode(contentIndex)"
  477. style="margin-top: 20px;"
  478. v-if="addForm.setting.length > 1 && addForm.canOp"
  479. ></i>
  480. </el-col>
  481. </el-row>
  482. </div>
  483. <el-link type="primary" class="el-icon-plus" :disabled="!addForm.canOp" :underline="false" @click='addContent()'>添加内容</el-link>
  484. </el-form-item>
  485. <el-form-item label="所属客服" prop="appCustomerIds" label-width="100px">
  486. <div>
  487. <el-button size="medium" icon="el-icon-circle-plus-outline" plain :disabled="!addForm.canOp"
  488. @click="openSelectCustomerPage">请选择客服</el-button>
  489. </div>
  490. <div>
  491. <el-tag style="margin-left: 5px" size="medium" :key="s.id" v-for="s in customerConfig.selected" closable
  492. :disable-transitions="false" @close="handleCloseCustomer(s)">
  493. <span>{{ s.memberName }}</span>
  494. </el-tag>
  495. </div>
  496. <div style="
  497. color: #999;
  498. font-size: 14px;
  499. display: flex;
  500. align-items: center;
  501. ">
  502. <i class="el-icon-info"></i>
  503. 选择了客服之后,此科普小节的完课 消息
  504. 只作用于此客服
  505. </div>
  506. </el-form-item>
  507. </el-form>
  508. <div slot="footer" class="dialog-footer">
  509. <el-button type="primary" v-if="addForm.canOp" @click="submitForm">确 定</el-button>
  510. <el-button @click="cancel">取 消</el-button>
  511. </div>
  512. </el-dialog>
  513. <!-- 选择app客服角色 -->
  514. <el-dialog title="选择客服" :visible.sync="customerConfig.visible" width="1300px" append-to-body>
  515. <CusRoleList
  516. :selected="customerConfig.selected"
  517. @confirm="customerSelectedConfirmCallForBind"
  518. @selectionChange="customerSelectionChangeCallForBind"
  519. />
  520. </el-dialog>
  521. </div>
  522. </template>
  523. <script>
  524. import {
  525. findList,
  526. // exportList,
  527. add,
  528. edit,
  529. getById,
  530. deleteByIds,
  531. } from "@/api/app/course/courseFinishTemp";
  532. import { courseList, videoList } from "@/api/app/course";
  533. import { getPackageOptions,} from "@/api/app/pkg";
  534. import ImageUpload from "@/views/qw/sop/ImageUpload.vue";
  535. import CusRoleList from '@/views/app/user/CusRoleList.vue';
  536. import {listRole} from '@/api/app/user/userList';
  537. export default {
  538. name: "CourseFinishTemp",
  539. components: { ImageUpload, CusRoleList, },
  540. data() {
  541. return {
  542. //上传语音的遮罩层
  543. voiceLoading: false,
  544. uploadUrl: process.env.VUE_APP_BASE_API + "/app/common/uploadOSS2",
  545. uploadUrlByVoice: process.env.VUE_APP_BASE_API + "/app/common/uploadOSSByHOOKVoice",
  546. videoLoading: false,
  547. videoOptionsLoading: false,
  548. courseLoading: false,
  549. videoOptions: [],
  550. sysFsSopWatchStatus: [],
  551. // 状态字典
  552. statusOptions: [],
  553. courseList: [],
  554. videoList: [],
  555. pkgList: [],
  556. queryCourseList: [],
  557. queryVideoList: [],
  558. // 遮罩层
  559. loading: true,
  560. // 导出遮罩层
  561. exportLoading: false,
  562. // 选中数组
  563. ids: [],
  564. // 非单个禁用
  565. single: true,
  566. // 非多个禁用
  567. multiple: true,
  568. // 显示搜索条件
  569. showSearch: true,
  570. formType: 1,
  571. // 总条数
  572. total: 0,
  573. // 完课模板表格数据
  574. courseFinishTempList: [],
  575. //插件版
  576. sysQwSopAiContentType: [],
  577. // 查询参数
  578. queryParams: {
  579. pageNum: 1,
  580. pageSize: 10,
  581. name: null,
  582. status: null,
  583. courseId: null,
  584. videoId: null
  585. },
  586. // 表单校验
  587. rules: {
  588. name: [
  589. { required: true, message: '模板名称不能为空', trigger: 'submit' }
  590. ],
  591. status: [
  592. { required: true, message: '状态不能为空', trigger: 'submit' }
  593. ],
  594. courseId: [
  595. { required: true, message: '科普/课节/类型不能为空', trigger: 'submit' }
  596. ],
  597. videoId: [
  598. { required: true, message: '科普/课节/类型不能为空', trigger: 'submit' }
  599. ],
  600. courseType: [
  601. { required: true, message: '科普/课节/类型不能为空', trigger: 'submit' }
  602. ],
  603. },
  604. finishTempList: [],
  605. //用来记录上次选择部门的数组
  606. addForm: {
  607. visible: false,
  608. title: '',
  609. canOp: true,
  610. id: null,
  611. name: null,
  612. status: 1,
  613. courseId: null,
  614. videoId: null,
  615. courseType: 3,//指定是完课消息
  616. setting: [],
  617. loading: false,
  618. },
  619. //选择客服对话框
  620. customerConfig: {
  621. visible: false,
  622. selected: [],
  623. },
  624. };
  625. },
  626. async created() {
  627. this.getList();
  628. this.getDicts("app_normal_disabled_status").then((response) => {
  629. this.statusOptions = response.data;
  630. });
  631. this.getDicts("app_sop_plugin_settingType").then(response => {
  632. this.sysQwSopAiContentType = response.data;
  633. });
  634. this.getDicts("app_sop_watch_status").then(response => {
  635. this.sysFsSopWatchStatus = response.data;
  636. });
  637. let courseList = await this.getCourseList(null, null);
  638. this.queryCourseList = courseList.list;
  639. let defaultPkg = await this.getPkgList(null, null);
  640. this.pkgList = defaultPkg.data;
  641. },
  642. methods: {
  643. //--------------------------------------------------------------列表 begin---------------------------------------------------------
  644. /**
  645. * 查询完课模板列表
  646. */
  647. getList() {
  648. this.loading = true;
  649. findList(this.queryParams).then((response) => {
  650. this.courseFinishTempList = response.rows;
  651. this.total = response.total;
  652. this.loading = false;
  653. });
  654. },
  655. /**
  656. * 搜索按钮操作
  657. */
  658. handleQuery() {
  659. this.queryParams.pageNum = 1;
  660. this.getList();
  661. },
  662. /**
  663. * 重置按钮操作
  664. */
  665. resetQuery() {
  666. this.resetForm("queryForm");
  667. this.handleQuery();
  668. },
  669. /**
  670. * 多选框选中数据
  671. * @param selection
  672. */
  673. handleSelectionChange(selection) {
  674. this.ids = selection.map((item) => item.id);
  675. this.single = selection.length !== 1;
  676. this.multiple = !selection.length;
  677. },
  678. /**
  679. * 新增按钮操作
  680. */
  681. async handleAdd() {
  682. this.reset();
  683. this.addContent();
  684. this.addForm.title = "添加完课模板";
  685. this.addForm.loading = true;
  686. this.addForm.visible = true;
  687. let courseList = await this.getCourseList(null, null);
  688. this.courseList = courseList.list;
  689. this.addForm.loading = false;
  690. },
  691. /**
  692. * 修改按钮操作
  693. * @param row
  694. */
  695. async handleUpdate(row, canOp) {
  696. this.reset();
  697. const id = row.id;
  698. this.addForm.title = canOp ? "修改完课模板" : "完课模板详情";
  699. this.addForm.canOp = canOp;
  700. this.addForm.loading = true;
  701. this.addForm.visible = true;
  702. let info = await getById(id);
  703. for (let key in this.addForm) {
  704. if (info.data.hasOwnProperty(key) && key !== 'setting') {
  705. this.addForm[key] = (key === 'status' ? parseInt(info.data.status) : info.data[key]);
  706. }
  707. }
  708. this.addForm.setting = JSON.parse(info.data.setting);
  709. this.addForm.setting.forEach(async st => {
  710. if (st.contentType == 20) {
  711. if (!st.packageId) {
  712. this.$set(st, 'meta', {
  713. pkgLoading: false,
  714. pkgList: []
  715. });
  716. } else {
  717. let pkgList = await this.getPkgList(null, st.packageId);
  718. this.$set(st, 'meta', {
  719. pkgLoading: false,
  720. pkgList: pkgList.data,
  721. });
  722. }
  723. }
  724. })
  725. if (info.data.appCustomerIds) {
  726. let userInfo = await listRole({ids: info.data.appCustomerIds});
  727. this.customerConfig.selected = userInfo.rows;
  728. }
  729. if (this.addForm.courseId) {
  730. let courseList = await this.getCourseList(null, this.addForm.courseId);
  731. this.courseList = courseList.list;
  732. let videoList = await this.getVideoList(this.addForm.courseId, null);
  733. this.videoList = videoList.list;
  734. }
  735. this.addForm.loading = false;
  736. },
  737. /**
  738. * 删除按钮操作
  739. * @param row
  740. */
  741. handleDelete(row) {
  742. const ids = row.id || this.ids;
  743. this.$confirm(
  744. '是否确认删除完课模板ID为"' + ids + '"的数据项?',
  745. "警告",
  746. {
  747. confirmButtonText: "确定",
  748. cancelButtonText: "取消",
  749. type: "warning",
  750. }
  751. )
  752. .then(async () => {
  753. await deleteByIds(ids);
  754. this.getList();
  755. this.msgSuccess("删除成功");
  756. })
  757. .catch(() => { });
  758. },
  759. //--------------------------------------------------------------新增/编辑 begin---------------------------------------------------------
  760. customerSelectedConfirmCallForBind() {
  761. this.customerConfig.visible = false;
  762. },
  763. /**
  764. * 选中的用户列表
  765. * @param list
  766. */
  767. customerSelectionChangeCallForBind(config) {
  768. let selected = config.selected;
  769. //单行操作
  770. if (config.mode === 0) {
  771. let row = config.row;
  772. if (selected) {
  773. if (!this.customerConfig.selected.some(s => s.id == row.id)) {
  774. this.customerConfig.selected.push(row)
  775. }
  776. } else {
  777. this.customerConfig.selected = this.customerConfig.selected.filter(m => m.id != row.id);
  778. }
  779. }
  780. //操作当前页所有行
  781. else {
  782. let list = config.rows;
  783. if (selected) {
  784. list.map(l => {
  785. if (!this.customerConfig.selected.some(s => s.id == l.id)) {
  786. this.customerConfig.selected.push(l)
  787. }
  788. });
  789. } else {
  790. list.map(l => {
  791. for (let i = 0; i < this.customerConfig.selected.length; i++) {
  792. if (l.id == this.customerConfig.selected[i].id) {
  793. this.customerConfig.selected.splice(i, 1);
  794. }
  795. }
  796. })
  797. }
  798. }
  799. },
  800. /**
  801. * 提交按钮
  802. */
  803. submitForm() {
  804. this.$refs["addForm"].validate(async (valid) => {
  805. if (valid) {
  806. if (this.checkData(this.addForm)) {
  807. let data = JSON.parse(JSON.stringify(this.addForm));
  808. data.setting.forEach(ele => {
  809. delete ele.meta;
  810. });
  811. data['appCustomerIds'] = this.customerConfig.selected.map(s => s.id).join(',');
  812. data['contentType'] = data.type;
  813. data['setting'] = JSON.stringify(data.setting);
  814. if (data.id != null) {
  815. await edit(data);
  816. this.msgSuccess("修改成功");
  817. this.reset();
  818. this.getList();
  819. } else {
  820. await add(data);
  821. this.msgSuccess("新增成功");
  822. this.reset();
  823. this.getList();
  824. }
  825. }
  826. }
  827. });
  828. },
  829. /**
  830. * 校验数据
  831. * @param data
  832. */
  833. checkData(data) {
  834. if (this.isEmpty(data.name)) {
  835. this.$message.error("模板名称不能为空")
  836. return false;
  837. }
  838. if (this.isEmpty(data.status)) {
  839. this.$message.error("状态不能为空")
  840. return false;
  841. }
  842. if (this.isEmpty(data.courseId)) {
  843. this.$message.error("请选择科普")
  844. return false;
  845. }
  846. if (this.isEmpty(data.videoId)) {
  847. this.$message.error("请选择课节")
  848. return false;
  849. }
  850. if (this.isEmpty(data.courseType)) {
  851. this.$message.error("消息类型不能为空")
  852. return false;
  853. }
  854. if (data.setting.length == 0) {
  855. this.$message.error("请添加规则!")
  856. return false;
  857. }
  858. for (let i = 0; i < data.setting.length; i++) {
  859. let setting = data.setting[i];
  860. if (setting.contentType == 11 && this.isEmpty(setting.value)) {
  861. this.$message.error("内容不能为空")
  862. return false;
  863. }
  864. if (setting.contentType == 12 && this.isEmpty(setting.value)) {
  865. this.$message.error("语音文本不能为空")
  866. return false;
  867. }
  868. if (setting.contentType == 2 && this.isEmpty(setting.imgUrl)) {
  869. this.$message.error("图片不能为空")
  870. return false;
  871. }
  872. if (9 == setting.contentType && this.isEmpty(setting.linkTitle)) {
  873. this.$message.error("链接标题不能为空")
  874. return false;
  875. }
  876. if (9 == setting.contentType && this.isEmpty(setting.linkDescribe)) {
  877. this.$message.error("链接描述不能为空")
  878. return false;
  879. }
  880. if (9 == setting.contentType && this.isEmpty(setting.linkImageUrl)) {
  881. this.$message.error("链接图片不能为空")
  882. return false;
  883. }
  884. if (20 == setting.contentType && this.isEmpty(setting.packageId)) {
  885. this.$message.error("疗法不能为空")
  886. return false;
  887. }
  888. }
  889. return true;
  890. },
  891. /**
  892. * 取消按钮
  893. */
  894. cancel() {
  895. this.reset();
  896. },
  897. /**
  898. * 表单重置
  899. */
  900. reset() {
  901. this.addForm = {
  902. visible: false,
  903. title: '',
  904. canOp: true,
  905. id: null,
  906. name: null,
  907. status: 1,
  908. courseId: null,
  909. videoId: null,
  910. courseType: 3,
  911. setting: [],
  912. loading: false,
  913. };
  914. this.customerConfig.selected = [];
  915. this.resetForm("addForm");
  916. },
  917. /**
  918. * 科普小节变化
  919. * @param mode 模式,0-查询列表,1-新增编辑页
  920. */
  921. videoIdChange(mode) {
  922. if (mode == 1) {
  923. //选择了科普小节则 默认绑上
  924. let selectedVideo;
  925. // 查找选中的科普对应的 label
  926. if (this.addForm.videoId) {
  927. selectedVideo = this.videoList.find(course => parseInt(course.dictValue) == this.addForm.videoId);
  928. }
  929. for (let i = 0; i < this.addForm.setting.length; i++) {
  930. let contentItem = this.addForm.setting[i];
  931. //科普消息-文本内容
  932. if (contentItem == 11) {
  933. this.$set(contentItem, 'isBindUrl', '2');
  934. }
  935. //如果是科普的才上
  936. if (selectedVideo && this.addForm.videoId != null) {
  937. if (contentItem.contentType == 9) {
  938. this.$set(contentItem, 'linkDescribe', selectedVideo.dictLabel);
  939. }
  940. }
  941. }
  942. }
  943. },
  944. /**
  945. * 疗法下拉搜索
  946. * @param keyword
  947. * @param content
  948. */
  949. async handlePkgRemoteSearch(keyword, content) {
  950. content.meta.pkgLoading = true;
  951. // 模拟远程接口请求(替换为你的真实接口)
  952. let cl = await this.getPkgList(this.isEmpty(keyword) ? null : keyword, null);
  953. content.meta.pkgLoading = false;
  954. content.meta.pkgList = cl.data;
  955. },
  956. /**
  957. * 删除内容节点
  958. * @param index
  959. */
  960. delContentNode(index){
  961. this.addForm.setting.splice(index, 1)
  962. },
  963. /**
  964. * 添加规则内容
  965. */
  966. addContent() {
  967. let content = {
  968. contentType: '11',//默认文本类型,其余类型暂时禁用
  969. value: '',
  970. };
  971. this.addForm.setting.push(content);
  972. this.handleContentTypeChange();
  973. },
  974. /**
  975. * 内容类别切换
  976. */
  977. handleContentTypeChange() {
  978. //获取选中的科普
  979. const selectedCourse = this.addForm.courseId != null
  980. ? this.courseList.find(course => parseInt(course.dictValue) == this.addForm.courseId)
  981. : null;
  982. //获取选中的视频
  983. const selectedVideo = this.addForm.videoId != null
  984. ? this.videoList.find(course => parseInt(course.dictValue) === this.addForm.videoId)
  985. : null;
  986. for (let i = 0; i < this.addForm.setting.length; i++) {
  987. let contentItem = this.addForm.setting[i];
  988. // 科普
  989. if (Number(contentItem.contentType) === 9) {
  990. // 处理科普链接标题/封面
  991. if (selectedCourse) {
  992. this.$set(contentItem, 'linkTitle', selectedCourse.dictLabel);
  993. this.$set(contentItem, 'linkImageUrl', selectedCourse.dictImgUrl);
  994. }
  995. // 处理视频链接描述
  996. if (selectedVideo) {
  997. this.$set(contentItem, 'linkDescribe', selectedVideo.dictLabel);
  998. }
  999. }
  1000. // 疗法
  1001. else if (Number(contentItem.contentType) === 20) {
  1002. if (!contentItem.packageId) {
  1003. this.$set(contentItem, 'meta', {
  1004. pkgLoading: false,
  1005. pkgList: this.pkgList,
  1006. });
  1007. } else {
  1008. let pkgList = this.getPkgList(null, contentItem.packageId);
  1009. this.$set(contentItem, 'meta', {
  1010. pkgLoading: false,
  1011. pkgList: pkgList.data,
  1012. });
  1013. }
  1014. }
  1015. }
  1016. },
  1017. /**
  1018. * 疗法变动处理
  1019. * @param content
  1020. */
  1021. async pkgChangeUpdate(content) {
  1022. //将当前规则已选中的视频小节清空
  1023. this.$set(content, 'packageName', null);
  1024. this.$set(content, 'packageImgUrl', null);
  1025. if (content.packageId) {
  1026. // 查找选中疗法的信息
  1027. const selectedPkg = content.meta.pkgList.find(pkg => parseInt(pkg.packageId) === content.packageId);
  1028. this.$set(content, 'packageName', selectedPkg.packageName);
  1029. this.$set(content, 'packageImgUrl', selectedPkg.imgUrl);
  1030. }
  1031. },
  1032. /**
  1033. * 打开选择客服的对话框
  1034. */
  1035. openSelectCustomerPage() {
  1036. this.customerConfig.visible = true;
  1037. },
  1038. /**
  1039. * 删除客服
  1040. * @param list
  1041. */
  1042. handleCloseCustomer(node) {
  1043. const index = this.customerConfig.selected.findIndex(t => t == node);
  1044. if (index !== -1) {
  1045. this.customerConfig.selected.splice(index, 1);
  1046. }
  1047. },
  1048. //--------------------------------------------------------------公共 begin---------------------------------------------------------
  1049. /**
  1050. * 获取科普信息
  1051. */
  1052. async getCourseList(keyword, selectedId) {
  1053. let data = {
  1054. keyword: keyword,
  1055. selectedId: selectedId,
  1056. pageNum: 1,
  1057. pageSize: 50,
  1058. };
  1059. return await courseList(data);
  1060. },
  1061. /**
  1062. * 科普变动处理
  1063. * @param mode 模式,0-查询列表,1-新增编辑页
  1064. */
  1065. async courseChangeUpdate(mode) {
  1066. if (mode == 1) {
  1067. //将当前规则已选中的视频小节清空
  1068. this.addForm.videoId = null;
  1069. this.videoList = [];
  1070. // 查找选中的科普的信息
  1071. const selectedCourse = this.courseList.find(course => parseInt(course.dictValue) == this.addForm.courseId);
  1072. for (let i = 0; i < this.addForm.setting.length; i++) {
  1073. let contentItem = this.addForm.setting[i];
  1074. this.$set(contentItem, 'linkTitle', null);
  1075. this.$set(contentItem, 'linkDescribe', null);
  1076. this.$set(contentItem, 'linkImageUrl', null);
  1077. //如果是科普才上
  1078. if (selectedCourse && this.addForm.courseId) {
  1079. //响应式直接给链接的标题/封面上值
  1080. if (contentItem.contentType == 9) {
  1081. this.$set(contentItem, 'linkTitle', selectedCourse.dictLabel);
  1082. this.$set(contentItem, 'linkImageUrl', selectedCourse.dictImgUrl);
  1083. }
  1084. }
  1085. }
  1086. if (this.addForm.courseId) {
  1087. let vl = await this.getVideoList(this.addForm.courseId, null);
  1088. this.videoList = vl.list;
  1089. } else {
  1090. let cl = await this.getCourseList(null, null);
  1091. this.courseList = cl.list;
  1092. }
  1093. } else {
  1094. if (this.queryParams.courseId) {
  1095. let vl = await this.getVideoList(this.queryParams.courseId, null);
  1096. this.queryVideoList = vl.list;
  1097. } else {
  1098. this.queryParams.videoId = null;
  1099. let cl = await this.getCourseList(null, null);
  1100. this.queryCourseList = cl.list;
  1101. let vl = await this.getVideoList(null, null);
  1102. this.queryVideoList = vl.list;
  1103. }
  1104. }
  1105. },
  1106. /**
  1107. * 科普下拉远程搜索
  1108. * @param {*} keyword 搜索关键字
  1109. * @param {*} mode 模式,0-查询列表,1-新增编辑页
  1110. */
  1111. async handleCourseRemoteSearch(keyword, mode) {
  1112. this.courseLoading = true;
  1113. // 模拟远程接口请求(替换为你的真实接口)
  1114. let cl = await this.getCourseList(this.isEmpty(keyword) ? null : keyword, null);
  1115. this.courseLoading = false;
  1116. if (1 == mode) {
  1117. this.courseList = cl.list;
  1118. } else {
  1119. this.queryCourseList = cl.list;
  1120. }
  1121. },
  1122. /**
  1123. * 根据科普获取视频小节
  1124. * @param courseId
  1125. * @param title
  1126. */
  1127. async getVideoList(courseId, title) {
  1128. return await videoList({
  1129. courseId: courseId,
  1130. title: title
  1131. });
  1132. },
  1133. /**
  1134. * 获取疗信息
  1135. */
  1136. async getPkgList(keyword, selectedId) {
  1137. let data = {
  1138. keyword: keyword,
  1139. metaId: selectedId,
  1140. limit: 50
  1141. };
  1142. return await getPackageOptions(data);
  1143. },
  1144. /**
  1145. * 空校验
  1146. * @param obj
  1147. */
  1148. isEmpty(obj) {
  1149. return obj === null || obj === undefined || obj.length === 0;
  1150. },
  1151. },
  1152. };
  1153. </script>