index.vue 57 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791
  1. <template>
  2. <div class="app-container">
  3. <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="100px">
  4. <el-form-item label="看课方式" prop="sendType">
  5. <el-select v-model="queryParams.sendType" placeholder="选择看课方式" size="small" @change="handleSendTypeChange">
  6. <el-option
  7. v-for="dict in sendTypeOptions"
  8. :key="dict.dictValue"
  9. :label="dict.dictLabel"
  10. :value="dict.dictValue"
  11. />
  12. </el-select>
  13. </el-form-item>
  14. <!-- <el-form-item label="项目" prop="project">-->
  15. <!-- <el-select v-model="queryParams.project" placeholder="请选择项目" clearable size="small" >-->
  16. <!-- <el-option-->
  17. <!-- v-for="item in projectOptions"-->
  18. <!-- :key="item.dictValue"-->
  19. <!-- :label="item.dictLabel"-->
  20. <!-- :value="item.dictValue"-->
  21. <!-- />-->
  22. <!-- </el-select>-->
  23. <!-- </el-form-item>-->
  24. <el-form-item label="会员昵称" prop="nickName" v-if="queryParams.sendType == 1">
  25. <el-input
  26. v-model="queryParams.nickName"
  27. placeholder="请输入会员昵称"
  28. clearable
  29. size="small"
  30. @keyup.enter.native="handleQuery"
  31. />
  32. </el-form-item>
  33. <el-form-item label="会员id" prop="userId" v-if="queryParams.sendType == 1">
  34. <el-input
  35. v-model="queryParams.userId"
  36. placeholder="请输入会员昵称"
  37. clearable
  38. size="small"
  39. @keyup.enter.native="handleQuery"
  40. />
  41. </el-form-item>
  42. <el-form-item label="企微客户昵称" prop="nickName" v-if="queryParams.sendType == 2">
  43. <el-input
  44. v-model="queryParams.externalUserName"
  45. placeholder="请输入企微客户昵称"
  46. clearable
  47. size="small"
  48. @keyup.enter.native="handleQuery"
  49. />
  50. </el-form-item>
  51. <!-- 营期名称 periodIds-->
  52. <el-form-item label="营期名称" prop="periodId">
  53. <el-select filterable v-model="queryParams.periodId" placeholder="请选择营期名称" clearable size="small">
  54. <el-option
  55. v-for="dict in scheduleLists"
  56. :key="dict.dictValue"
  57. :label="dict.dictLabel"
  58. :value="dict.dictValue"
  59. />
  60. </el-select>
  61. </el-form-item>
  62. <el-form-item label="课程" prop="courseId">
  63. <el-select filterable v-model="queryParams.courseId" placeholder="请选择课程" clearable size="small" @change="courseChange(queryParams.courseId)">
  64. <el-option
  65. v-for="dict in courseLists"
  66. :key="dict.dictValue"
  67. :label="dict.dictLabel"
  68. :value="dict.dictValue"
  69. />
  70. </el-select>
  71. </el-form-item>
  72. <el-form-item label="小节" prop="videoId">
  73. <el-select filterable v-model="queryParams.videoId" placeholder="请选择小节" clearable size="small">
  74. <el-option
  75. v-for="dict in videoList"
  76. :key="dict.dictValue"
  77. :label="dict.dictLabel"
  78. :value="dict.dictValue"
  79. />
  80. </el-select>
  81. </el-form-item>
  82. <el-form-item v-if="companyName === undefined || companyName === 1" label="所属销售" prop="companyUserId">
  83. <el-select v-model="queryParams.companyUserId" clearable filterable remote
  84. placeholder="请输入关键词" :remote-method="loadCompanyUserOptions"
  85. v-select-load-more="loadMoreCompanyUserOptions"
  86. @change="handleCompanyUserChange"
  87. :loading="companyUserOptionsLoading"
  88. @visible-change="handleCompanyUserDropdownVisible"
  89. >
  90. <el-option
  91. v-for="item in companyUserOptions"
  92. :key="item.dictValue"
  93. :label="item.dictLabel"
  94. :value="item.dictValue">
  95. </el-option>
  96. </el-select>
  97. </el-form-item>
  98. <el-form-item label="所属部门" prop="deptId">
  99. <treeselect style="width:220px" v-model="queryParams.deptId" :options="deptOptions" :show-count="true" placeholder="请选择所属部门" />
  100. </el-form-item>
  101. <el-form-item v-if="companyName==2" label="所属销售" prop="companyUserId">
  102. <el-select v-model="queryParams.companyUserId" clearable filterable remote
  103. placeholder="请输入关键词"
  104. v-select-load-more="loadMoreCompanyUserOptions"
  105. @change="handleCompanyUserChange"
  106. @visible-change="handleQwUserDropdownVisible"
  107. :loading="companyUserOptionsLoading">
  108. <el-option
  109. v-for="item in companyUserOptionsByAll"
  110. :key="item.dictValue"
  111. :label="item.dictLabel"
  112. :value="item.dictValue">
  113. </el-option>
  114. </el-select>
  115. </el-form-item>
  116. <el-form-item label="所属企微" prop="qwUserName" v-if="queryParams.companyUserId && (queryParams.sendType == 2)">
  117. <el-select v-model="queryParams.qwUserName"
  118. clearable filterable
  119. placeholder="请输入关键词"
  120. @visible-change="handleQwUserDropdownVisible"
  121. :loading="qwUserOptionsLoading">
  122. <el-option
  123. v-for="item in qwUserOptions"
  124. :key="item.qwUserId"
  125. :label="item.qwUserName"
  126. :value="item.qwUserName">
  127. </el-option>
  128. </el-select>
  129. </el-form-item>
  130. <!-- sop名称 -->
  131. <el-form-item label="SOP名称" prop="sopId" v-if="queryParams.sendType == 2">
  132. <el-autocomplete
  133. v-model="sopSearchText"
  134. :fetch-suggestions="querySopAsync"
  135. placeholder="请输入SOP名称"
  136. clearable
  137. size="small"
  138. style="width: 200px"
  139. @select="handleSopSelect"
  140. @clear="handleSopClear"
  141. :trigger-on-focus="false"
  142. >
  143. <template slot-scope="{ item }">
  144. <div class="sop-item">
  145. <span class="sop-name">{{ item.name }}</span>
  146. </div>
  147. </template>
  148. </el-autocomplete>
  149. </el-form-item>
  150. <!-- 营期时间 -->
  151. <!-- <el-form-item label="营期时间" prop="scheduleTime">
  152. <el-input
  153. v-model="scheduleTimeText"
  154. placeholder="请选择营期时间"
  155. readonly
  156. @click.native="showScheduleCalendar = true"
  157. />
  158. <calendar
  159. v-model="scheduleTime"
  160. mode="during"
  161. :show.sync="showScheduleCalendar"
  162. @change="handleScheduleTimeChange"
  163. :key="scheduleCalendarKey"
  164. />
  165. </el-form-item>-->
  166. <el-form-item label="营期时间" prop="scheduleTime">
  167. <el-date-picker
  168. v-model="scheduleTimeText"
  169. type="daterange"
  170. range-separator="至"
  171. start-placeholder="开始日期"
  172. end-placeholder="结束日期"
  173. value-format="yyyy-MM-dd"
  174. style="width: 240px"
  175. @change="handleScheduleTimeChange"
  176. />
  177. </el-form-item>
  178. <!-- 创建时间 -->
  179. <!-- <el-form-item label="创建时间" prop="createTime">
  180. <el-input
  181. v-model="createTimeText"
  182. placeholder="请选择创建时间"
  183. readonly
  184. @click.native="showCreateCalendar = true"
  185. />
  186. <calendar
  187. v-model="createTime"
  188. mode="during"
  189. :show.sync="showCreateCalendar"
  190. @change="createChange"
  191. :key="createCalendarKey"
  192. />
  193. </el-form-item> -->
  194. <el-form-item label="创建时间" prop="createTime">
  195. <el-date-picker
  196. v-model="createTimeText"
  197. type="datetimerange"
  198. range-separator="至"
  199. start-placeholder="开始日期"
  200. end-placeholder="结束日期"
  201. value-format="yyyy-MM-dd HH:mm:ss"
  202. @change="createChange"
  203. :default-time="['00:00:00', '23:59:59']"
  204. />
  205. </el-form-item>
  206. <!-- 最新更新时间 -->
  207. <!-- <el-form-item label="最新更新时间" prop="updateTime">
  208. <el-input
  209. v-model="updateTimeText"
  210. placeholder="请选择更新时间"
  211. readonly
  212. @click.native="showUpdateCalendar = true"
  213. />
  214. <calendar
  215. v-model="updateTime"
  216. mode="during"
  217. :show.sync="showUpdateCalendar"
  218. @change="updateChange"
  219. :key="updateCalendarKey"
  220. />
  221. </el-form-item> -->
  222. <el-form-item label="最新更新时间" prop="updateTime">
  223. <el-date-picker
  224. v-model="updateTimeText"
  225. type="daterange"
  226. range-separator="至"
  227. start-placeholder="开始日期"
  228. end-placeholder="结束日期"
  229. value-format="yyyy-MM-dd"
  230. style="width: 240px"
  231. @change="updateChange"
  232. />
  233. </el-form-item>
  234. <!-- 进线时间 -->
  235. <!-- <el-form-item label="进线时间" prop="qecCreateTime">
  236. <el-input
  237. v-model="qecCreateTimeText"
  238. placeholder="请选择进线时间"
  239. readonly
  240. @click.native="showQecCalendar = true"
  241. />
  242. <calendar
  243. v-model="qecCreateTime"
  244. mode="during"
  245. :show.sync="showQecCalendar"
  246. @change="qecCreateTimeChange"
  247. :key="qecCalendarKey"
  248. />
  249. </el-form-item> -->
  250. <el-form-item label="进线时间" prop="qecCreateTime">
  251. <el-date-picker
  252. v-model="qecCreateTimeText"
  253. type="daterange"
  254. range-separator="至"
  255. start-placeholder="开始日期"
  256. end-placeholder="结束日期"
  257. value-format="yyyy-MM-dd"
  258. style="width: 240px"
  259. @change="qecCreateTimeChange"
  260. />
  261. </el-form-item>
  262. <el-form-item label="营期课程时间" prop="periodTime" v-if="queryParams.sendType==1">
  263. <el-date-picker
  264. v-model="periodTimeText"
  265. type="datetimerange"
  266. align="right"
  267. unlink-panels
  268. value-format="yyyy-MM-dd HH:mm:ss"
  269. range-separator="至"
  270. start-placeholder="开始日期"
  271. end-placeholder="结束日期"
  272. :picker-options="pickerOptions"
  273. @change="qecPeriodTimeChange"
  274. :default-time="['00:00:00', '23:59:59']">
  275. </el-date-picker>
  276. </el-form-item>
  277. <!-- <el-form-item label="是否注册" prop="isVip">-->
  278. <!-- <el-select-->
  279. <!-- filterable-->
  280. <!-- v-model="queryParams.isVip"-->
  281. <!-- placeholder="请选择是否注册"-->
  282. <!-- clearable size="small">-->
  283. <!-- <el-option-->
  284. <!-- v-for="dict in isVipList"-->
  285. <!-- :key="dict.dictValue"-->
  286. <!-- :label="dict.dictLabel"-->
  287. <!-- :value="dict.dictValue"-->
  288. <!-- />-->
  289. <!-- </el-select>-->
  290. <!-- </el-form-item>-->
  291. <el-form-item>
  292. <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
  293. <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
  294. </el-form-item>
  295. </el-form>
  296. <el-row :gutter="10" class="mb8">
  297. <el-col :span="1.5">
  298. <el-button
  299. type="warning"
  300. plain
  301. icon="el-icon-download"
  302. size="mini"
  303. :loading="exportLoading"
  304. @click="handleExport"
  305. v-hasPermi="['course:courseWatchLog:export']"
  306. >导出</el-button>
  307. <el-col :span="1.5" v-if="queryParams.sendType == 2">
  308. <el-button
  309. type="primary"
  310. plain
  311. size="mini"
  312. @click="addUserTag"
  313. v-hasPermi="['qw:externalContact:addTag']"
  314. >批量添加标签</el-button>
  315. </el-col>
  316. <el-col :span="1.5" v-if="queryParams.sendType == 2" >
  317. <el-button
  318. type="primary"
  319. plain
  320. size="mini"
  321. @click="delUserTag"
  322. v-hasPermi="['qw:externalContact:delTag']"
  323. >批量移除标签</el-button>
  324. </el-col>
  325. </el-col>
  326. <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
  327. </el-row>
  328. <el-tabs type="card" v-model="activeName" @tab-click="handleClickX">
  329. <el-tab-pane label="全部" name="00"></el-tab-pane>
  330. <el-tab-pane v-for="(item,index) in logTypeOptions" :label="item.dictLabel" :name="item.dictValue"></el-tab-pane>
  331. </el-tabs>
  332. <el-table border v-loading="loading" :data="courseWatchLogList" @selection-change="handleSelectionChange">
  333. <el-table-column type="selection" width="55" align="center" />
  334. <el-table-column label="记录编号" align="center" prop="logId" />
  335. <el-table-column label="用户昵称" align="center">
  336. <template slot-scope="scope">
  337. {{ queryParams.sendType=='1' ? scope.row.fsNickName : scope.row.externalUserName }}
  338. </template>
  339. </el-table-column>
  340. <el-table-column label="头像" align="center">
  341. <template slot-scope="scope">
  342. <img v-if="queryParams.sendType=='1'" :src="scope.row.fsAvatar" style="width:50px;height:50px" />
  343. <img v-else :src="scope.row.externalUserAvatar" style="width:50px;height:50px" />
  344. </template>
  345. </el-table-column>
  346. <el-table-column label="会员ID" align="center" prop="userId" />
  347. <el-table-column label="会员昵称" align="center" prop="fsNickName">
  348. <template slot-scope="scope">
  349. <div style="display: flex;white-space: nowrap">
  350. <div style="margin: auto">
  351. {{scope.row.fsNickName}}
  352. </div>
  353. <el-popover
  354. placement="right"
  355. title=""
  356. trigger="hover">
  357. <img slot="reference" :src="scope.row.fsAvatar" style="width: 30px;height: 30px">
  358. <img :src="scope.row.fsAvatar" style="max-width: 200px;max-height: 200px">
  359. </el-popover>
  360. </div>
  361. </template>
  362. </el-table-column>
  363. <el-table-column label="营期名称" align="center" prop="periodIdName" v-if="this.queryParams.sendType==1" />
  364. <el-table-column label="课程名称" align="center" prop="courseName" />
  365. <el-table-column label="小节名称" align="center" prop="videoName" />
  366. <el-table-column label="记录类型" align="center" prop="logType">
  367. <template slot-scope="scope">
  368. <dict-tag :options="logTypeOptions" :value="scope.row.logType"/>
  369. </template>
  370. </el-table-column>
  371. <el-table-column label="播放时长" align="center" prop="duration" />
  372. <el-table-column label="所属销售" align="center" prop="companyUserName" />
  373. <!-- <el-table-column label="所属公司" align="center" prop="companyName" />-->
  374. <el-table-column label="所属企微" align="center" prop="qwUserName" v-if="queryParams.sendType==2" />
  375. <!-- <el-table-column label="所属发送方式" align="center" prop="sendType" />-->
  376. <el-table-column label="创建时间" align="center" prop="createTime" />
  377. <el-table-column label="更新时间" align="center" prop="updateTime" />
  378. <el-table-column label="完课时间" align="center" prop="finishTime" />
  379. <el-table-column label="营期时间" align="center" prop="campPeriodTime" />
  380. <el-table-column label="进线时间" align="center" prop="qecCreateTime" />
  381. <el-table-column label="是否领奖" align="center" prop="rewardType" >
  382. <template slot-scope="scope">
  383. <el-tag
  384. :type="scope.row.rewardType ? 'success' : 'info'"
  385. effect="plain"
  386. >
  387. {{ scope.row.rewardType ? '已领取' : '未领取' }}
  388. </el-tag>
  389. </template>
  390. </el-table-column>
  391. <el-table-column
  392. fixed="right"
  393. label="操作"
  394. width="100">
  395. <template slot-scope="scope">
  396. <el-button @click="openAnswerLogFun(scope.row)" type="text" size="small">答题记录</el-button>
  397. <el-button @click="openRedLogFun(scope.row)" type="text" size="small">红包记录</el-button>
  398. </template>
  399. </el-table-column>
  400. </el-table>
  401. <pagination
  402. v-show="total>0"
  403. :total="total"
  404. :page.sync="queryParams.pageNum"
  405. :limit.sync="queryParams.pageSize"
  406. @pagination="getList"
  407. />
  408. <el-drawer title="答题记录" :visible.sync="openAnswerLog" size="70%" append-to-body>
  409. <el-table border v-loading="" :data="answerLogsList">
  410. <el-table-column label="会员用户" align="center" prop="userName">
  411. <template slot-scope="scope">
  412. <div style="display: flex;white-space: nowrap">
  413. <div style="margin: auto">
  414. {{ scope.row.userName }}
  415. </div>
  416. <el-popover
  417. placement="right"
  418. title=""
  419. trigger="hover">
  420. <img slot="reference" :src="scope.row.fsAvatar" style="width: 30px;height: 30px">
  421. <img :src="scope.row.fsAvatar" style="max-width: 200px;max-height: 200px">
  422. </el-popover>
  423. </div>
  424. </template>
  425. </el-table-column>
  426. <el-table-column label="课程名称" align="center" prop="courseName"/>
  427. <el-table-column label="小节名称" align="center" prop="videoName"/>
  428. <el-table-column label="是否全部正确" align="center" prop="isRight">
  429. <template slot-scope="scope">
  430. <dict-tag :options="sysCompanyOr" :value="scope.row.isRight"></dict-tag>
  431. </template>
  432. </el-table-column>
  433. <el-table-column label="销售名称" align="center" prop="companyUserName"/>
  434. <el-table-column label="企微员工名称" align="center" prop="qwUserName"/>
  435. <el-table-column label="公司名称" align="center" prop="companyName"/>
  436. <el-table-column label="创建时间" align="center" prop="createTime"/>
  437. </el-table>
  438. <pagination
  439. v-show="answerLogTotal>0"
  440. :total="answerLogTotal"
  441. :page.sync="answerLogQueryParams.pageNum"
  442. :limit.sync="answerLogQueryParams.pageSize"
  443. @pagination="answerLogList"
  444. />
  445. </el-drawer>
  446. <el-drawer title="红包记录" :visible.sync="openRedLog" size="70%" append-to-body>
  447. <el-table border v-loading="" :data="redLogsList">
  448. <el-table-column type="selection" width="55" align="center" />
  449. <el-table-column label="记录编号" align="center" prop="logId" />
  450. <el-table-column label="批次单号" align="center" prop="outBatchNo" />
  451. <el-table-column label="课程名称" align="center" prop="courseId" >
  452. <template slot-scope="scope">
  453. <span prop="status" v-for="(item, index) in courseLists" v-if="scope.row.courseId==item.dictValue">{{item.dictLabel}}</span>
  454. </template>
  455. </el-table-column>
  456. <el-table-column label="小节名称" align="center" prop="title" />
  457. <!-- <el-table-column label="会员id" align="center" prop="userId" />-->
  458. <el-table-column label="会员用户" align="center" prop="fsNickName">
  459. <template slot-scope="scope">
  460. <div style="display: flex;white-space: nowrap">
  461. <div style="margin: auto">
  462. {{scope.row.fsNickName}}
  463. </div>
  464. <el-popover
  465. placement="right"
  466. title=""
  467. trigger="hover">
  468. <img slot="reference" :src="scope.row.fsAvatar" style="width: 30px;height: 30px">
  469. <img :src="scope.row.fsAvatar" style="max-width: 200px;max-height: 200px">
  470. </el-popover>
  471. </div>
  472. </template>
  473. </el-table-column>
  474. <!-- <el-table-column label="会员电话" align="center" prop="phone" />-->
  475. <!-- <el-table-column label="所属销售" align="center" prop="companyUserName" />-->
  476. <!-- <el-table-column label="所属公司" align="center" prop="companyName" />-->
  477. <el-table-column label="转账金额" align="center" prop="amount" />
  478. <el-table-column label="状态" align="center" prop="status" >
  479. <template slot-scope="scope">
  480. <el-tag>
  481. {{
  482. scope.row.status === 0 ? "发送中" :
  483. scope.row.status === 2 ? "待补发" :
  484. "已完成"
  485. }}
  486. </el-tag>
  487. </template>
  488. </el-table-column>
  489. <el-table-column label="所属企微" align="center" prop="qwUserName" />
  490. <el-table-column label="创建时间" align="center" prop="createTime" />
  491. </el-table>
  492. <pagination
  493. v-show="redLogTotal>0"
  494. :total="redLogTotal"
  495. :page.sync="redLogQueryParams.pageNum"
  496. :limit.sync="redLogQueryParams.pageSize"
  497. @pagination="redLogList"
  498. />
  499. </el-drawer>
  500. <el-dialog title="批量添加标签" :visible.sync="tagOpen" width="800px" append-to-body>
  501. <div>搜索标签:
  502. <el-input v-model="tagChange.tagName" placeholder="请输入标签名称" clearable size="small" style="width: 200px;margin-right: 10px" />
  503. <el-button type="primary" icon="el-icon-search" size="mini" @click="handleSearchTags(tagChange.tagName)">搜索</el-button>
  504. <el-button type="primary" icon="el-icon-plus" size="mini" @click="cancelSearchTags">重置</el-button>
  505. </div>
  506. <el-form ref="form" :model="addTagFormByWatch" label-width="80px">
  507. <div v-for="item in tagGroupList" :key="item.id" >
  508. <div style="font-size: 20px;margin-top: 20px;margin-bottom: 20px;">
  509. <span class="name-background">{{ item.name }}</span>
  510. </div>
  511. <!-- 添加外层滚动容器 -->
  512. <div class="scroll-wrapper">
  513. <div class="tag-container">
  514. <a
  515. v-for="tagItem in item.tag"
  516. class="tag-box"
  517. @click="tagSelection(tagItem)"
  518. :class="{ 'tag-selected': tagItem.isSelected }"
  519. >
  520. {{ tagItem.name }}
  521. </a>
  522. </div>
  523. </div>
  524. <!-- <div class="tag-container">-->
  525. <!-- <a-->
  526. <!-- v-for="tagItem in item.tag"-->
  527. <!-- class="tag-box"-->
  528. <!-- @click="tagSelection(tagItem)"-->
  529. <!-- :class="{ 'tag-selected': tagItem.isSelected }"-->
  530. <!-- >-->
  531. <!-- {{ tagItem.name }}-->
  532. <!-- </a>-->
  533. <!-- </div>-->
  534. </div>
  535. </el-form>
  536. <pagination
  537. v-show="tagTotal>0"
  538. :total="tagTotal"
  539. :page.sync="queryTagParams.pageNum"
  540. :limit.sync="queryTagParams.pageSize"
  541. @pagination="getPageListTagGroup"
  542. />
  543. <div slot="footer" class="dialog-footer">
  544. <el-button type="primary" @click="addTagSubmitForm()">确 定</el-button>
  545. <el-button @click="addTagCancel">取 消</el-button>
  546. </div>
  547. </el-dialog>
  548. <el-dialog title="批量移除标签" :visible.sync="tagDelOpen" width="800px" append-to-body>
  549. <div>搜索标签:
  550. <el-input v-model="tagChange.tagName" placeholder="请输入标签名称" clearable size="small" style="width: 200px;margin-right: 10px" />
  551. <el-button type="primary" icon="el-icon-search" size="mini" @click="handleSearchTags(tagChange.tagName)">搜索</el-button>
  552. <el-button type="primary" icon="el-icon-plus" size="mini" @click="cancelSearchTags">重置</el-button>
  553. </div>
  554. <el-form ref="form" :model="addTagFormByWatch" label-width="80px">
  555. <div v-for="item in tagGroupList" :key="item.id" >
  556. <div style="font-size: 20px;margin-top: 20px;margin-bottom: 20px;">
  557. <span class="name-background">{{ item.name }}</span>
  558. </div>
  559. <!-- 添加外层滚动容器 -->
  560. <div class="scroll-wrapper">
  561. <div class="tag-container">
  562. <a
  563. v-for="tagItem in item.tag"
  564. class="tag-box"
  565. @click="tagSelection(tagItem)"
  566. :class="{ 'tag-selected': tagItem.isSelected }"
  567. >
  568. {{ tagItem.name }}
  569. </a>
  570. </div>
  571. </div>
  572. </div>
  573. </el-form>
  574. <pagination
  575. v-show="tagTotal>0"
  576. :total="tagTotal"
  577. :page.sync="queryTagParams.pageNum"
  578. :limit.sync="queryTagParams.pageSize"
  579. @pagination="getPageListTagGroup"
  580. />
  581. <div slot="footer" class="dialog-footer">
  582. <el-button type="primary" @click="tagDelSubmitForm()">确 定</el-button>
  583. <el-button @click="DelTagCancel">取 消</el-button>
  584. </div>
  585. </el-dialog>
  586. <el-dialog
  587. :title="resultTitle"
  588. :visible.sync="resultDialogVisible"
  589. width="50%"
  590. custom-class="feedback-dialog"
  591. >
  592. <pre style="white-space: pre-wrap; font-family: inherit;">{{ resultMessage }}</pre>
  593. <span slot="footer" class="dialog-footer">
  594. <el-button @click="resultDialogVisible = false">关闭</el-button>
  595. </span>
  596. </el-dialog>
  597. </div>
  598. </template>
  599. <script>
  600. import { listCourseWatchLog, getCourseWatchLog, delCourseWatchLog, addCourseWatchLog, updateCourseWatchLog, exportCourseWatchLog } from "@/api/course/courseWatchLog";
  601. import {listPeriodLabel} from "@/api/course/userCoursePeriod";
  602. import {courseList, listCourseRedPacketLog, videoList} from '@/api/course/courseRedPacketLog'
  603. import {listLogs} from "@/api/course/courseAnswerlogs";
  604. import {allListTagGroup} from "../../../api/qw/tagGroup";
  605. import {searchTags} from "../../../api/qw/tag";
  606. import {addTagByWatch, delTagByWatch} from "../../../api/qw/externalContact";
  607. import Vue from 'vue'
  608. import Calendar from 'vue-mobile-calendar'
  609. import {infoSop} from "@/api/qw/sop";
  610. import {getCompanyUserListLikeName} from "../../../api/company/companyUser";
  611. import {getQwList} from "@/api/qw/qwUser";
  612. import {treeselect} from "@/api/company/companyDept";
  613. import Treeselect from "@riophae/vue-treeselect";
  614. import "@riophae/vue-treeselect/dist/vue-treeselect.css";
  615. Vue.use(Calendar)
  616. export default {
  617. name: "CourseWatchLog",
  618. components: {Treeselect },
  619. data() {
  620. return {
  621. companyUserFirstLoad: true, // 首次加载标志
  622. qwUserFirstLoad: true, // 首次加载标志
  623. companyUserDropdownVisible: false, // 下拉框显示状态
  624. qwUserDropdownVisible: false, // 下拉框显示状态
  625. companyName:process.env.VUE_APP_COURSE_COMPANY_NAME,
  626. // 日历 key 控制刷新
  627. scheduleCalendarKey: 0,
  628. createCalendarKey: 0,
  629. updateCalendarKey: 0,
  630. qecCalendarKey: 0,
  631. periodTimeKey: 0,
  632. createTimeText: '',
  633. scheduleTimeText: '', // 新增
  634. updateTimeText: '', // 新增
  635. qecCreateTimeText: '', // 新增
  636. periodTimeText: '', // 营期课程时间
  637. scheduleTime: [], // 改为数组
  638. createTime: [], // 改为数组
  639. updateTime: [], // 改为数组
  640. qecCreateTime: [], // 改为数组
  641. periodTime: [], // 改为数组
  642. // 控制日历显隐
  643. showScheduleCalendar: false,
  644. showCreateCalendar: false,
  645. showUpdateCalendar: false,
  646. showQecCalendar: false,
  647. resultDialogVisible: false,
  648. resultMessage: '',
  649. resultTitle:'',
  650. activeName:"2",
  651. pickerOptions: {
  652. disabledDate(time) {
  653. // 获取13天前的日期(加上今天就是14天)
  654. const sixDaysAgo = new Date();
  655. sixDaysAgo.setDate(sixDaysAgo.getDate() - 13);
  656. sixDaysAgo.setHours(0, 0, 0, 0);
  657. // 获取明天的日期(不包括今天)
  658. const tomorrow = new Date();
  659. tomorrow.setDate(tomorrow.getDate() + 1);
  660. tomorrow.setHours(0, 0, 0, 0);
  661. return time.getTime() < sixDaysAgo.getTime() || time.getTime() >= tomorrow.getTime();
  662. }
  663. },
  664. courseLists:[],
  665. deptOptions: undefined,
  666. scheduleLists:[], // 营期名称
  667. videoList:[],
  668. logTypeOptions:[],
  669. projectOptions:[],
  670. sendTypeOptions:[{
  671. dictLabel:"会员",dictValue:'1'
  672. },
  673. {
  674. dictLabel:"企微",dictValue:'2'
  675. }
  676. ],
  677. // 遮罩层
  678. loading: true,
  679. // 导出遮罩层
  680. exportLoading: false,
  681. // 选中数组
  682. ids: [],
  683. // 非单个禁用
  684. single: true,
  685. // 非多个禁用
  686. multiple: true,
  687. // 显示搜索条件
  688. showSearch: true,
  689. // 总条数
  690. total: 0,
  691. // 短链课程看课记录表格数据
  692. courseWatchLogList: [],
  693. // 弹出层标题
  694. title: "",
  695. // 是否显示弹出层
  696. open: false,
  697. tagOpen:false,
  698. //标签弹窗选择
  699. tagChange:{
  700. open:false,
  701. index:null,
  702. },
  703. addTagFormByWatch:{
  704. logId:[],
  705. tagIds:[]
  706. },
  707. tagGroupList: [],
  708. tagTotal:0,
  709. tagDelOpen:false,
  710. queryTagParams:{
  711. pageNum: 1,
  712. pageSize: 5,
  713. total:0,
  714. name:null,
  715. corpId:null,
  716. },
  717. //答题记录
  718. openAnswerLog: false,
  719. loadingAnswerLog: true,
  720. answerLogsList: [],
  721. answerLogTotal: 0,
  722. answerLogQueryParams: {
  723. pageNum: 1,
  724. pageSize: 10,
  725. },
  726. //红包记录
  727. openRedLog: false,
  728. loadingRedLog: true,
  729. redLogsList: [],
  730. redLogTotal: 0,
  731. redLogQueryParams: {
  732. pageNum: 1,
  733. pageSize: 10,
  734. },
  735. isVipList: [
  736. { dictLabel: '是', dictValue: 1 },
  737. { dictLabel: '否', dictValue: 0 }
  738. ],
  739. // SOP搜索相关
  740. sopSearchText: '', // SOP搜索框显示的文本
  741. selectedSopId: null, // 选中的SOP ID
  742. // 查询参数
  743. queryParams: {
  744. pageNum: 1,
  745. pageSize: 10,
  746. project: null,
  747. userId: null,
  748. nickName: null,
  749. videoId: null,
  750. logType: 2,
  751. qwExternalContactId: null,
  752. externalUserName:null,
  753. duration: null,
  754. qwUserId: null,
  755. qwUserName: null,
  756. companyUserId: null,
  757. deptId: null,
  758. companyId: null,
  759. courseId: null,
  760. periodId:null,
  761. sTime:null,
  762. eTime:null,
  763. upSTime:null,
  764. upETime:null,
  765. qecSTime:null,
  766. qecETime:null,
  767. periodSTime:null,
  768. periodETime:null,
  769. scheduleStartTime: null,
  770. scheduleEndTime: null,
  771. sendType:process.env.VUE_APP_COURSE_DEFAULT,
  772. isVip: null,
  773. sopId: null, // sopId
  774. },
  775. // 表单参数
  776. form: {},
  777. // 表单校验
  778. rules: {
  779. },
  780. // 员工选项列表
  781. companyUserOptionsParams: {
  782. name: undefined,
  783. hasNextPage: false,
  784. pageNum: 1,
  785. pageSize: 10
  786. },
  787. companyUserOptions: [],
  788. companyUserOptionsByAll: [],
  789. companyUserOptionsLoading: false,
  790. // 企微信息
  791. qwUserOptions: [],
  792. // 员工选项列表
  793. qwUserOptionsParams: {
  794. name: undefined,
  795. hasNextPage: false,
  796. pageNum: 1,
  797. pageSize: 10
  798. },
  799. qwUserOptionsLoading: false,
  800. };
  801. },
  802. created() {
  803. courseList().then(response => {
  804. this.courseLists = response.list;
  805. });
  806. this.getTreeselect();
  807. this.getDicts("sys_course_watch_log_type_new").then(response => {
  808. this.logTypeOptions = response.data;
  809. });
  810. this.getDicts("sys_course_project").then(response => {
  811. this.projectOptions = response.data;
  812. });
  813. // 查询营期名称
  814. listPeriodLabel().then(response => {
  815. this.scheduleLists = response.rows;
  816. });
  817. // 设置默认当天时间 xgb 防止频繁查询大量数据
  818. this.setToday();
  819. this.getList();
  820. this.getCompanyUserListLikeName(true);
  821. this.loading=false;
  822. },
  823. methods: {
  824. getTreeselect() {
  825. treeselect().then((response) => {
  826. this.deptOptions = response.data;
  827. });
  828. },
  829. // 添加辅助方法
  830. isEmptyArray(arr) {
  831. return !arr || arr.length === 0;
  832. },
  833. setToday(){
  834. const today = new Date();
  835. const todayStart = new Date(today - 60*60*24*7*1000) ;
  836. todayStart.setHours(0, 0, 0, 0);
  837. const todayEnd = new Date(today);
  838. todayEnd.setHours(23, 59, 59, 999);
  839. this.createTimeText = [this.formatDate(todayStart), this.formatDate(todayEnd)];
  840. this.queryParams.sTime = this.formatDate(todayStart);
  841. this.queryParams.eTime = this.formatDate(todayEnd);
  842. },
  843. /**
  844. * 处理所属销售下拉框显示状态变化
  845. */
  846. handleCompanyUserDropdownVisible(visible) {
  847. this.companyUserDropdownVisible = visible;
  848. if (visible && this.companyUserFirstLoad) {
  849. // 首次展开下拉框时加载数据
  850. this.companyUserFirstLoad = false;
  851. this.loadCompanyUserOptions('');
  852. } else if (visible && this.companyUserOptions.length === 0) {
  853. // 下拉框显示但无数据时重新加载
  854. this.loadCompanyUserOptions('');
  855. }
  856. },
  857. /**
  858. * 处理所属企微下拉框显示状态变化
  859. */
  860. handleQwUserDropdownVisible(visible) {
  861. this.qwUserDropdownVisible = visible;
  862. if (visible && this.qwUserFirstLoad) {
  863. // 首次展开下拉框时加载数据
  864. this.qwUserFirstLoad = false;
  865. this.loadQwUserOptions('');
  866. } else if (visible && this.qwUserOptions.length === 0) {
  867. // 下拉框显示但无数据时重新加载
  868. this.loadQwUserOptions('');
  869. }
  870. },
  871. /**
  872. * 当销售被选择
  873. * 级联更新企微列表
  874. */
  875. handleCompanyUserChange(companyUserId){
  876. if (companyUserId) {
  877. // 清空当前企微选择
  878. this.queryParams.qwUserName = "";
  879. this.qwUserOptions = [];
  880. this.qwUserOptionsParams.pageNum = 1;
  881. // 重新加载企微列表,传入销售ID作为过滤条件
  882. this.getQwList();
  883. } else {
  884. // 清空销售时也清空企微
  885. this.queryParams.qwUserName = "";
  886. this.qwUserOptions = [];
  887. this.qwUserOptionsParams.pageNum = 1;
  888. }
  889. },
  890. /**
  891. * 根据名称模糊查询用户列表
  892. * @param query 参数
  893. */
  894. loadCompanyUserOptions(query) {
  895. this.companyUserOptions = [];
  896. this.companyUserOptionsParams.pageNum = 1
  897. this.companyUserOptionsParams.name = query
  898. this.companyUserOptionsLoading = true;
  899. this.getCompanyUserListLikeName()
  900. },
  901. /**
  902. * 根据条件查询企微列表
  903. * @param query 参数
  904. */
  905. loadQwUserOptions(query) {
  906. this.qwUserOptions = [];
  907. this.qwUserOptionsParams.pageNum = 1
  908. // 将搜索关键词设置到queryParams中
  909. this.queryParams.qwUserName = query
  910. this.qwUserOptionsLoading = true;
  911. this.getQwList()
  912. },
  913. /**
  914. * 加载更多员工选项
  915. */
  916. loadMoreCompanyUserOptions() {
  917. if (!this.companyUserOptionsParams.hasNextPage) {
  918. return;
  919. }
  920. this.companyUserOptionsParams.pageNum += 1
  921. this.getCompanyUserListLikeName()
  922. },
  923. /**
  924. * 获取员工列表
  925. */
  926. getCompanyUserListLikeName(isAll) {
  927. if (isAll){
  928. this.companyUserOptionsParams.pageSize = 200;
  929. getCompanyUserListLikeName(this.companyUserOptionsParams).then(response => {
  930. this.companyUserOptionsByAll = [...this.companyUserOptions, ...response.data.list]
  931. this.companyUserOptionsParams.hasNextPage = response.data.hasNextPage
  932. this.companyUserOptionsLoading = false;
  933. });
  934. }else {
  935. this.companyUserOptionsParams.pageSize = 10;
  936. getCompanyUserListLikeName(this.companyUserOptionsParams).then(response => {
  937. this.companyUserOptions = [...this.companyUserOptions, ...response.data.list]
  938. this.companyUserOptionsParams.hasNextPage = response.data.hasNextPage
  939. this.companyUserOptionsLoading = false;
  940. });
  941. }
  942. },
  943. getQwList() {
  944. const params = {
  945. name: this.qwUserOptionsParams.name,
  946. pageNum: this.qwUserOptionsParams.pageNum,
  947. pageSize: this.qwUserOptionsParams.pageSize,
  948. // 添加销售ID作为过滤条件
  949. companyUserId: this.queryParams.companyUserId || null,
  950. disableCompanyId: 1
  951. };
  952. console.log("企微参数", this.queryParams);
  953. getQwList(params).then(response => {
  954. if (this.qwUserOptionsParams.pageNum === 1) {
  955. this.qwUserOptions = response.rows || [];
  956. } else {
  957. this.qwUserOptions = [...this.qwUserOptions, ...(response.rows || [])];
  958. }
  959. this.qwUserOptionsParams.hasNextPage = response.rows && response.rows.length >= this.qwUserOptionsParams.pageSize;
  960. }).finally(()=>{
  961. this.qwUserOptionsLoading = false;
  962. })
  963. },
  964. /**
  965. * 加载更多员工选项
  966. */
  967. loadMoreQwUserOptions() {
  968. if (!this.qwUserOptionsParams.hasNextPage) {
  969. return;
  970. }
  971. this.qwUserOptionsParams.pageNum += 1
  972. this.getQwList()
  973. },
  974. // 重置日历组件
  975. resetCalendars() {
  976. this.scheduleTime = [];
  977. this.createTime = [];
  978. this.updateTime = [];
  979. this.qecCreateTime = [];
  980. this.periodTime = [];
  981. this.scheduleTimeText = [];
  982. this.createTimeText = [];
  983. this.updateTimeText = [];
  984. this.qecCreateTimeText = [];
  985. this.periodTimeText = [];
  986. // 强制刷新日历组件
  987. this.scheduleCalendarKey++;
  988. this.createCalendarKey++;
  989. this.updateCalendarKey++;
  990. this.qecCalendarKey++;
  991. this.periodTimeKey++;
  992. },
  993. formatDateRange(dates) {
  994. if (!dates || dates.length < 2) return '';
  995. return dates.map(date => date.format('YYYY-MM-DD')).join(' ~ ');
  996. },
  997. courseChange(row){
  998. this.queryParams.videoId=null;
  999. if(row === ''){
  1000. this.videoList=[];
  1001. return
  1002. }
  1003. videoList(row).then(response => {
  1004. this.videoList=response.list
  1005. });
  1006. },
  1007. checkDateRangeLimit(dateRange) {
  1008. if (dateRange && dateRange.length >= 2) {
  1009. const startDate = new Date(dateRange[0]);
  1010. const endDate = new Date(dateRange[1]);
  1011. // 设置时间为当天开始,避免时间部分影响计算
  1012. startDate.setHours(0, 0, 0, 0);
  1013. endDate.setHours(0, 0, 0, 0);
  1014. const timeDiff = Math.abs(endDate - startDate);
  1015. const diffDays = Math.ceil(timeDiff / (1000 * 60 * 60 * 24));
  1016. if (diffDays > 31) { // maxDays-1 因为包含起始日
  1017. this.$message.warning('时间区间不能超过一个月');
  1018. return false;
  1019. }
  1020. }
  1021. return true;
  1022. },
  1023. // 营期时间
  1024. handleScheduleTimeChange(scheduleTime) {
  1025. if (scheduleTime && scheduleTime.length >= 2) {
  1026. if(!this.checkDateRangeLimit(scheduleTime)){
  1027. this.scheduleTimeText = null;
  1028. this.queryParams.scheduleStartTime=null;
  1029. this.queryParams.scheduleStartTime=null;
  1030. return;
  1031. }
  1032. // this.scheduleTimeText = this.formatDateRange(scheduleTime);
  1033. this.queryParams.scheduleStartTime = scheduleTime[0] || null;
  1034. this.queryParams.scheduleEndTime = scheduleTime[1] || null;
  1035. console.log(this.queryParams.scheduleStartTime)
  1036. console.log(this.queryParams.scheduleEndTime)
  1037. } else {
  1038. this.scheduleTimeText = '';
  1039. this.queryParams.scheduleStartTime = null;
  1040. this.queryParams.scheduleEndTime = null;
  1041. }
  1042. },
  1043. // 创建时间
  1044. createChange(createTime) {
  1045. if (createTime && createTime.length >= 2) {
  1046. if(!this.checkDateRangeLimit(createTime)){
  1047. this.createTimeText = null;
  1048. this.queryParams.sTime=null;
  1049. this.queryParams.eTime=null;
  1050. return;
  1051. }
  1052. // this.createTimeText = this.formatDateRange(createTime);
  1053. this.queryParams.sTime = this.formatDate(createTime[0]) || null;
  1054. this.queryParams.eTime = this.formatDate(createTime[1]) || null;
  1055. } else {
  1056. this.createTimeText = '';
  1057. this.queryParams.sTime = null;
  1058. this.queryParams.eTime = null;
  1059. }
  1060. },
  1061. // 更新时间
  1062. updateChange(updateTime) {
  1063. if (updateTime && updateTime.length >= 2) {
  1064. if(!this.checkDateRangeLimit(updateTime)){
  1065. this.updateTimeText = null;
  1066. this.queryParams.upSTime=null;
  1067. this.queryParams.upETime=null;
  1068. return;
  1069. }
  1070. // this.updateTimeText = this.formatDateRange(updateTime);
  1071. this.queryParams.upSTime = updateTime[0] || null;
  1072. this.queryParams.upETime = updateTime[1] || null;
  1073. } else {
  1074. this.updateTimeText = '';
  1075. this.queryParams.upSTime = null;
  1076. this.queryParams.upETime = null;
  1077. }
  1078. },
  1079. // 进线时间
  1080. qecCreateTimeChange(qecCreateTime) {
  1081. if (qecCreateTime && qecCreateTime.length >= 2) {
  1082. // 检查选择的日期范围是否超过7天(包括起始和结束日期)
  1083. const startDate = new Date(qecCreateTime[0]);
  1084. const endDate = new Date(qecCreateTime[1]);
  1085. // 设置时间为当天开始,避免时间部分影响计算
  1086. startDate.setHours(0, 0, 0, 0);
  1087. endDate.setHours(0, 0, 0, 0);
  1088. const timeDiff = Math.abs(endDate - startDate);
  1089. const diffDays = Math.ceil(timeDiff / (1000 * 60 * 60 * 24));
  1090. // 如果超过6天的范围(总共7天,包括起始日)
  1091. if (diffDays > 6) {
  1092. this.$message.error('进线时间选择范围不能超过7天');
  1093. // 清空选择
  1094. this.qecCreateTime = [];
  1095. this.qecCreateTimeText = [];
  1096. this.queryParams.qecSTime = null;
  1097. this.queryParams.qecETime = null;
  1098. this.qecCalendarKey++;
  1099. return;
  1100. }
  1101. // this.qecCreateTimeText = this.formatDateRange(qecCreateTime);
  1102. this.queryParams.qecSTime = qecCreateTime[0] || null;
  1103. this.queryParams.qecETime = qecCreateTime[1] || null;
  1104. console.log(this.queryParams.qecSTime);
  1105. console.log(this.queryParams.qecETime);
  1106. } else {
  1107. this.qecCreateTimeText = '';
  1108. this.queryParams.qecSTime = null;
  1109. this.queryParams.qecETime = null;
  1110. }
  1111. },
  1112. //营期课程时间
  1113. qecPeriodTimeChange(periodTime){
  1114. if (periodTime && periodTime.length >= 2) {
  1115. // 检查选择的日期范围是否超过7天(包括起始和结束日期)
  1116. const startDate = new Date(periodTime[0]);
  1117. const endDate = new Date(periodTime[1]);
  1118. // 设置时间为当天开始,避免时间部分影响计算
  1119. startDate.setHours(0, 0, 0, 0);
  1120. endDate.setHours(0, 0, 0, 0);
  1121. const timeDiff = Math.abs(endDate - startDate);
  1122. const diffDays = Math.ceil(timeDiff / (1000 * 60 * 60 * 24));
  1123. // 如果超过6天的范围(总共7天,包括起始日)
  1124. if (diffDays > 13) {
  1125. this.$message.error('时间选择范围不能超过14天');
  1126. // 清空选择
  1127. this.periodTime = [];
  1128. this.periodTimeText = [];
  1129. this.queryParams.periodSTime = null;
  1130. this.queryParams.periodETime = null;
  1131. this.periodTimeKey++;
  1132. return;
  1133. }
  1134. this.queryParams.periodSTime = this.formatDate(periodTime[0]) || null;
  1135. this.queryParams.periodETime = this.formatDate(periodTime[1]) || null;
  1136. } else {
  1137. this.periodTimeText = '';
  1138. this.queryParams.periodSTime = null;
  1139. this.queryParams.periodETime = null;
  1140. }
  1141. },
  1142. formatDate(date) {
  1143. if (!date) return ''
  1144. // 确保 date 是 Date 对象
  1145. let dateObj = date
  1146. if (typeof date === 'string') {
  1147. dateObj = new Date(date)
  1148. }
  1149. // 如果转换失败,返回空字符串
  1150. if (!(dateObj instanceof Date) || isNaN(dateObj.getTime())) {
  1151. return ''
  1152. }
  1153. // 使用更安全的格式化方法
  1154. const year = dateObj.getFullYear()
  1155. const month = String(dateObj.getMonth() + 1).padStart(2, '0')
  1156. const day = String(dateObj.getDate()).padStart(2, '0')
  1157. const hours = String(dateObj.getHours()).padStart(2, '0')
  1158. const minutes = String(dateObj.getMinutes()).padStart(2, '0')
  1159. const seconds = String(dateObj.getSeconds()).padStart(2, '0')
  1160. return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
  1161. },
  1162. handleClickX(tab,event){
  1163. this.activeName=tab.name;
  1164. if(tab.name=="00"){
  1165. this.queryParams.logType=null;
  1166. }else{
  1167. this.queryParams.logType=tab.name;
  1168. }
  1169. this.getList()
  1170. },
  1171. /** 查询短链课程看课记录列表 */
  1172. getList() {
  1173. // xgb 看课数据量太大必须限制时间
  1174. if (this.isEmptyArray(this.createTimeText) &&
  1175. this.isEmptyArray(this.updateTimeText) &&
  1176. this.isEmptyArray(this.scheduleTimeText)) {
  1177. this.$message.warning('请选择创建时间或营期时间或最新更新时间');
  1178. return;
  1179. }
  1180. this.loading = true;
  1181. if(this.queryParams.logType == "10"){
  1182. this.queryParams.logType = null;
  1183. }
  1184. listCourseWatchLog(this.queryParams).then(response => {
  1185. this.courseWatchLogList = response.rows;
  1186. this.total = response.total;
  1187. this.loading = false;
  1188. }).catch(() => {
  1189. this.loading = false;
  1190. });
  1191. },
  1192. // 取消按钮
  1193. cancel() {
  1194. this.open = false;
  1195. this.reset();
  1196. },
  1197. // 表单重置
  1198. reset() {
  1199. this.form = {
  1200. logId: null,
  1201. userId: null,
  1202. videoId: null,
  1203. logType: null,
  1204. createTime: null,
  1205. updateTime: null,
  1206. qwExternalContactId: null,
  1207. externalUserName:null,
  1208. duration: null,
  1209. qwUserId: null,
  1210. companyUserId: null,
  1211. companyId: null,
  1212. courseId: null,
  1213. periodId:null,
  1214. sTime:null,
  1215. eTime:null,
  1216. upSTime:null,
  1217. upETime:null,
  1218. qecSTime:null,
  1219. qecETime:null,
  1220. scheduleStartTime: null,
  1221. scheduleEndTime: null,
  1222. };
  1223. // 统一重置日历组件
  1224. this.resetCalendars();
  1225. this.resetForm("form");
  1226. },
  1227. /** 搜索按钮操作 */
  1228. handleQuery() {
  1229. this.queryParams.pageNum = 1;
  1230. this.getList();
  1231. },
  1232. /** 重置按钮操作 */
  1233. resetQuery() {
  1234. this.resetForm("queryForm");
  1235. this.queryParams.sTime = null;
  1236. this.queryParams.project = null;
  1237. this.queryParams.eTime = null;
  1238. this.queryParams.upSTime = null;
  1239. this.queryParams.upETime = null;
  1240. this.queryParams.qecSTime = null;
  1241. this.queryParams.qecETime = null;
  1242. this.queryParams.periodSTime = null;
  1243. this.queryParams.periodDTime = null;
  1244. this.queryParams.externalUserName=null;
  1245. this.queryParams.scheduleStartTime = null;
  1246. this.queryParams.scheduleEndTime = null;
  1247. this.queryParams.sopId = null; // 重置SOP ID
  1248. // 重置SOP搜索
  1249. this.handleSopClear();
  1250. // 统一重置日历组件
  1251. this.resetCalendars();
  1252. this.setToday();
  1253. this.handleQuery();
  1254. },
  1255. // 多选框选中数据
  1256. handleSelectionChange(selection) {
  1257. this.ids = selection.map(item => item.logId)
  1258. this.single = selection.length!==1
  1259. this.multiple = !selection.length
  1260. },
  1261. /** 新增按钮操作 */
  1262. handleAdd() {
  1263. this.reset();
  1264. this.open = true;
  1265. this.title = "添加短链课程看课记录";
  1266. },
  1267. /** 修改按钮操作 */
  1268. handleUpdate(row) {
  1269. this.reset();
  1270. const logId = row.logId || this.ids
  1271. getCourseWatchLog(logId).then(response => {
  1272. this.form = response.data;
  1273. this.open = true;
  1274. this.title = "修改短链课程看课记录";
  1275. });
  1276. },
  1277. /** 提交按钮 */
  1278. submitForm() {
  1279. this.$refs["form"].validate(valid => {
  1280. if (valid) {
  1281. if (this.form.logId != null) {
  1282. updateCourseWatchLog(this.form).then(response => {
  1283. this.msgSuccess("修改成功");
  1284. this.open = false;
  1285. this.getList();
  1286. });
  1287. } else {
  1288. addCourseWatchLog(this.form).then(response => {
  1289. this.msgSuccess("新增成功");
  1290. this.open = false;
  1291. this.getList();
  1292. });
  1293. }
  1294. }
  1295. });
  1296. },
  1297. /** 删除按钮操作 */
  1298. handleDelete(row) {
  1299. const logIds = row.logId || this.ids;
  1300. this.$confirm('是否确认删除短链课程看课记录编号为"' + logIds + '"的数据项?', "警告", {
  1301. confirmButtonText: "确定",
  1302. cancelButtonText: "取消",
  1303. type: "warning"
  1304. }).then(function() {
  1305. return delCourseWatchLog(logIds);
  1306. }).then(() => {
  1307. this.getList();
  1308. this.msgSuccess("删除成功");
  1309. }).catch(() => {});
  1310. },
  1311. /** 导出按钮操作 */
  1312. handleExport() {
  1313. // xgb 看课数据量太大必须限制时间
  1314. if (this.isEmptyArray(this.createTimeText) &&
  1315. this.isEmptyArray(this.updateTimeText) &&
  1316. this.isEmptyArray(this.scheduleTimeText)) {
  1317. this.$message.warning('请选择创建时间或营期时间或最新更新时间');
  1318. return;
  1319. }
  1320. const queryParams = this.queryParams;
  1321. this.$confirm('是否确认导出所有短链课程看课记录数据项?', "警告", {
  1322. confirmButtonText: "确定",
  1323. cancelButtonText: "取消",
  1324. type: "warning"
  1325. }).then(() => {
  1326. this.exportLoading = true;
  1327. return exportCourseWatchLog(queryParams);
  1328. }).then(response => {
  1329. this.download(response.msg);
  1330. this.exportLoading = false;
  1331. }).catch(() => {});
  1332. },
  1333. openAnswerLogFun(row) {
  1334. this.openAnswerLog = true;
  1335. this.answerLogQueryParams.watchLogId = row.logId;
  1336. this.answerLogList();
  1337. },
  1338. answerLogList() {
  1339. this.loadingAnswerLog = true;
  1340. listLogs(this.answerLogQueryParams).then(e => {
  1341. this.answerLogsList = e.rows;
  1342. this.answerLogTotal = e.total;
  1343. this.loadingAnswerLog = false;
  1344. })
  1345. },
  1346. openRedLogFun(row) {
  1347. this.openRedLog = true;
  1348. this.redLogQueryParams.watchLogId = row.logId;
  1349. this.redLogList();
  1350. },
  1351. redLogList() {
  1352. this.loadingRedLog = true;
  1353. console.info("-----index",this.redLogQueryParams)
  1354. listCourseRedPacketLog(this.redLogQueryParams).then(e => {
  1355. this.redLogsList = e.rows;
  1356. this.redLogTotal = e.total;
  1357. this.loadingRedLog = false;
  1358. })
  1359. },
  1360. handleSendTypeChange() {
  1361. // 如果是会员,清除一下选中的企微
  1362. if(this.queryParams.sendType == 1) {
  1363. this.queryParams.qwUserName = null;
  1364. }
  1365. setTimeout(() => {
  1366. this.handleQuery();
  1367. }, 200);
  1368. },
  1369. addUserTag(){
  1370. if(this.ids==null||this.ids==""){
  1371. return this.$message('请选择需要添加标签的客户');
  1372. }
  1373. this.getPageListTagGroup();
  1374. setTimeout(() => {
  1375. for (let i = 0; i < this.tagGroupList.length; i++) {
  1376. for (let x = 0; x < this.tagGroupList[i].tag.length; x++) {
  1377. this.tagGroupList[i].tag[x].isSelected=false;
  1378. }
  1379. }
  1380. }, 200);
  1381. this.tagOpen = true;
  1382. },
  1383. delUserTag(){
  1384. if(this.ids==null||this.ids==""){
  1385. return this.$message('请选择需要移除标签的客户');
  1386. }
  1387. this.getPageListTagGroup();
  1388. setTimeout(() => {
  1389. for (let i = 0; i < this.tagGroupList.length; i++) {
  1390. for (let x = 0; x < this.tagGroupList[i].tag.length; x++) {
  1391. this.tagGroupList[i].tag[x].isSelected=false;
  1392. }
  1393. }
  1394. }, 200);
  1395. this.tagDelOpen = true;
  1396. },
  1397. getPageListTagGroup(){
  1398. this.queryTagParams.corpId=this.queryParams.corpId
  1399. allListTagGroup(this.queryTagParams).then(response => {
  1400. this.tagGroupList = response.rows;
  1401. this.tagTotal = response.total;
  1402. });
  1403. },
  1404. tagSelection(row){
  1405. row.isSelected= !row.isSelected;
  1406. this.$forceUpdate();
  1407. },
  1408. handleSearchTags(name){
  1409. if (!name){
  1410. return this.$message.error("请输入要搜索的标签")
  1411. }
  1412. this.queryTagParams.name=name;
  1413. this.queryTagParams.corpId=this.queryParams.corpId;
  1414. searchTags(this.queryTagParams).then(response => {
  1415. this.tagGroupList = response.rows;
  1416. this.tagTotal = response.total;
  1417. });
  1418. // searchTags({name:name,corpId:this.queryParams.corpId}).then(response => {
  1419. // this.tagGroupList = response.rows;
  1420. // });
  1421. },
  1422. cancelSearchTags(){
  1423. this.resetSearchQueryTag()
  1424. this.getPageListTagGroup();
  1425. },
  1426. resetSearchQueryTag(){
  1427. this.tagChange.tagName=null;
  1428. this.queryTagParams= {
  1429. pageNum: 1,
  1430. pageSize: 5,
  1431. total:0,
  1432. name:null,
  1433. };
  1434. },
  1435. addTagSubmitForm(){
  1436. for (let i = 0; i < this.tagGroupList.length; i++) {
  1437. for (let x = 0; x < this.tagGroupList[i].tag.length; x++) {
  1438. if(this.tagGroupList[i].tag[x].isSelected==true){
  1439. this.addTagFormByWatch.tagIds.push(this.tagGroupList[i].tag[x].tagId)
  1440. }
  1441. }
  1442. }
  1443. if(this.addTagFormByWatch.tagIds==[]||this.addTagFormByWatch.tagIds==null||this.addTagFormByWatch.tagIds==""){
  1444. return this.$message('请选择标签');
  1445. }
  1446. this.addTagFormByWatch.logIds=this.ids;
  1447. let loadingRock = this.$loading({
  1448. lock: true,
  1449. text: '正在执行中请稍后~~请不要刷新页面!!',
  1450. spinner: 'el-icon-loading',
  1451. background: 'rgba(0, 0, 0, 0.7)'
  1452. });
  1453. addTagByWatch(this.addTagFormByWatch).then(response => {
  1454. // this.msgSuccess(response.msg);
  1455. this.resultMessage = response.msg;
  1456. this.resultDialogVisible = true; // 显示弹窗
  1457. this.resultTitle = '批量添加标签结果';
  1458. this.tagOpen = false;
  1459. loadingRock.close();
  1460. this.addTagFormByWatch={
  1461. logIds:[],
  1462. tagIds:[]
  1463. };
  1464. this.getList()
  1465. }).finally(res=>{
  1466. loadingRock.close();
  1467. });
  1468. },
  1469. tagDelSubmitForm(){
  1470. for (let i = 0; i < this.tagGroupList.length; i++) {
  1471. for (let x = 0; x < this.tagGroupList[i].tag.length; x++) {
  1472. if(this.tagGroupList[i].tag[x].isSelected==true){
  1473. this.addTagFormByWatch.tagIds.push(this.tagGroupList[i].tag[x].tagId)
  1474. }
  1475. }
  1476. }
  1477. if(this.addTagFormByWatch.tagIds==[]||this.addTagFormByWatch.tagIds==null||this.addTagFormByWatch.tagIds==""){
  1478. return this.$message('请选择标签');
  1479. }
  1480. this.addTagFormByWatch.corpId=this.queryParams.corpId
  1481. this.addTagFormByWatch.logIds=this.ids;
  1482. let loadingRock = this.$loading({
  1483. lock: true,
  1484. text: '正在执行中请稍后~~请不要刷新页面!!',
  1485. spinner: 'el-icon-loading',
  1486. background: 'rgba(0, 0, 0, 0.7)'
  1487. });
  1488. delTagByWatch(this.addTagFormByWatch).then(response => {
  1489. // this.msgSuccess(response.msg);
  1490. this.resultMessage = response.msg;
  1491. this.resultDialogVisible = true; // 显示弹窗
  1492. this.resultTitle = '批量移除标签结果';
  1493. this.tagDelOpen = false;
  1494. loadingRock.close();
  1495. this.addTagFormByWatch={
  1496. userIds:[],
  1497. tagIds:[]
  1498. };
  1499. this.getList()
  1500. }).finally(res=>{
  1501. loadingRock.close();
  1502. });
  1503. },
  1504. addTagCancel() {
  1505. this.tagOpen = false;
  1506. this.addTagFormByWatch={
  1507. logIds:[],
  1508. tagIds:[]
  1509. };
  1510. },
  1511. DelTagCancel() {
  1512. this.tagDelOpen = false;
  1513. this.addTagFormByWatch={
  1514. logIds:[],
  1515. tagIds:[]
  1516. };
  1517. },
  1518. /**
  1519. * 异步查询SOP列表
  1520. * @param {string} queryString - 查询字符串
  1521. * @param {function} callback - 回调函数
  1522. */
  1523. querySopAsync(queryString, callback) {
  1524. if (!queryString) {
  1525. callback([]);
  1526. return;
  1527. }
  1528. infoSop({ name: queryString }).then(response => {
  1529. if (response && response.rows) {
  1530. const suggestions = response.rows.map(item => ({
  1531. value: item.name,
  1532. id: item.id,
  1533. name: item.name
  1534. }));
  1535. callback(suggestions);
  1536. } else {
  1537. callback([]);
  1538. }
  1539. }).catch(error => {
  1540. console.error('通过sop查询失败:', error);
  1541. callback([]);
  1542. });
  1543. },
  1544. /**
  1545. * 选择SOP
  1546. * @param {object} item - 选中的SOP项
  1547. */
  1548. handleSopSelect(item) {
  1549. this.selectedSopId = item.id;
  1550. this.queryParams.sopId = item.id;
  1551. this.sopSearchText = item.name;
  1552. },
  1553. /**
  1554. * 清空SOP选择
  1555. */
  1556. handleSopClear() {
  1557. this.selectedSopId = null;
  1558. this.queryParams.sopId = null;
  1559. this.sopSearchText = '';
  1560. },
  1561. }
  1562. };
  1563. </script>
  1564. <style scoped>
  1565. /* CSS 样式 */
  1566. .tag-container {
  1567. display: flex;
  1568. flex-wrap: wrap; /* 超出宽度时自动换行 */
  1569. gap: 8px; /* 设置标签之间的间距 */
  1570. }
  1571. .name-background {
  1572. display: inline-block;
  1573. background-color: #abece6; /* 背景颜色 */
  1574. padding: 4px 8px; /* 调整内边距,让背景包裹文字 */
  1575. border-radius: 4px; /* 可选:设置圆角 */
  1576. }
  1577. /* CSS 样式 */
  1578. .tag-container {
  1579. display: flex;
  1580. flex-wrap: wrap; /* 超出宽度时自动换行 */
  1581. gap: 8px; /* 设置标签之间的间距 */
  1582. }
  1583. .name-background {
  1584. display: inline-block;
  1585. background-color: #abece6; /* 背景颜色 */
  1586. padding: 4px 8px; /* 调整内边距,让背景包裹文字 */
  1587. border-radius: 4px; /* 可选:设置圆角 */
  1588. }
  1589. .tag-box {
  1590. padding: 8px 12px;
  1591. border: 1px solid #989797;
  1592. border-radius: 4px;
  1593. cursor: pointer;
  1594. display: inline-block;
  1595. }
  1596. .tag-selected {
  1597. background-color: #00bc98;
  1598. color: #fff;
  1599. border-color: #00bc98;
  1600. }
  1601. .el-tag + .el-tag {
  1602. margin-left: 10px;
  1603. }
  1604. /* SOP搜索框样式 */
  1605. .sop-item {
  1606. display: flex;
  1607. align-items: center;
  1608. }
  1609. .sop-name {
  1610. font-size: 14px;
  1611. color: #606266;
  1612. }
  1613. .button-new-tag {
  1614. margin-left: 10px;
  1615. height: 32px;
  1616. line-height: 30px;
  1617. padding-top: 0;
  1618. padding-bottom: 0;
  1619. }
  1620. .input-new-tag {
  1621. width: 90px;
  1622. margin-left: 10px;
  1623. vertical-align: bottom;
  1624. }
  1625. /* 新增的滚动容器样式(不影响原有样式) */
  1626. .scroll-wrapper {
  1627. max-height: 130px; /* 大约三行的高度 */
  1628. overflow-y: auto; /* 垂直滚动 */
  1629. padding-right: 5px; /* 为滚动条留出空间 */
  1630. }
  1631. /* 美化滚动条(可选) */
  1632. .scroll-wrapper::-webkit-scrollbar {
  1633. width: 6px;
  1634. }
  1635. .scroll-wrapper::-webkit-scrollbar-thumb {
  1636. background: rgba(0, 0, 0, 0.2);
  1637. border-radius: 3px;
  1638. }
  1639. </style>