index.php 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656
  1. <?php
  2. use common\helpers\ComponentHelper;
  3. ComponentHelper::loadComponentView('com-share-pop');
  4. ?>
  5. <div id="app" v-cloak>
  6. <el-card shadow="never" style="border:0;min-width: 1000px;" body-style="background-color: #f3f3f3;padding: 10px 0 0;">
  7. <div slot="header" class="flex" style="align-items: center;justify-content: space-between;">
  8. <span>门店列表</span>
  9. <!-- <el-form size="small" :inline="true" :model="search" style="float: right;margin-top: -5px;">
  10. <el-form-item>
  11. <com-export-dialog :field_list='exportList' :params="search" @selected="confirmSubmit">
  12. </com-export-dialog>
  13. </el-form-item>
  14. </el-form> -->
  15. <div class="right_btns">
  16. <el-tooltip class="item" effect="dark" content="当小程序变更时, 可用于清空上一个小程序残存的二维码(推广码、收款码)" placement="bottom">
  17. <el-button type="warning" size="small" @click="cleanMiniCode" :loading="btnLoading">清除小程序码</el-button>
  18. </el-tooltip>
  19. <el-button type="primary" size="small" @click="addStore">添加门店</el-button>
  20. </div>
  21. </div>
  22. <div class="table-body">
  23. <el-alert style="margin-bottom:20px;" type="info" title="门店管理端登录入口链接:" :closable="false">
  24. <template>
  25. <span id="target">{{store_login_url}}</span>
  26. <el-button type="text" id="copy_btn" data-clipboard-action="copy" data-clipboard-target="#target" size="mini">复制链接 </el-button>
  27. </template>
  28. </el-alert>
  29. <div style="padding-bottom: 20px;">
  30. <div class="flex">
  31. <div class="flex" style="margin-right: 20px;">
  32. <div class="input-title" style="flex-shrink: 0;">门店名称</div>
  33. <el-input size="small" style="width: 130px;" placeholder="请输入门店名称" v-model="searchData.store_name"> </el-input>
  34. </div>
  35. <div class="flex" style="margin-right: 20px;">
  36. <div class="input-title" style="flex-shrink: 0;">店长</div>
  37. <el-input size="small" style="width: 130px;" placeholder="请输入店长姓名" v-model="searchData.storeowner">
  38. </el-input>
  39. </div>
  40. <div class="flex" style="margin-right: 20px;">
  41. <div class="input-title" style="flex-shrink: 0;">绑定会员</div>
  42. <el-input size="small" style="width: 130px;" placeholder="请输入会员名称" v-model="searchData.nickname">
  43. </el-input>
  44. </div>
  45. <div class="item-box" flex="dir:left cross:center">
  46. <div class="label">门店分类</div>
  47. <el-select v-model="searchData.cate_id" @change='searchs' class="select" size="small" style="width: 130px;">
  48. <el-option :key="0" label="所有分类" :value="0"></el-option>
  49. <el-option :key="item.id" :label="item.name" :value="item.id" v-for="item in categorys"></el-option>
  50. </el-select>
  51. </div>
  52. <div class="item-box" flex="dir:left cross:center">
  53. <div class="label">门店状态</div>
  54. <el-select v-model="searchData.status" @change='searchs' class="select" size="small" style="width: 130px;">
  55. <el-option :key="0" label="全部" :value="0"></el-option>
  56. <el-option :key="1" label="正常" :value="1"></el-option>
  57. <el-option :key="2" label="过期" :value="2"></el-option>
  58. <el-option :key="3" label="禁用" :value="3"></el-option>
  59. <el-option :key="3" label="冻结" :value="4"></el-option>
  60. </el-select>
  61. </div>
  62. <div class="item-box" flex="dir:left cross:center">
  63. <div class="label">有效时间排序</div>
  64. <el-select v-model="searchData.time_sort" @change='searchs' class="select" size="small" style="width: 130px;">
  65. <el-option :key="1" label="按门店有效时间升序" :value="1"></el-option>
  66. <el-option :key="2" label="按门店有效时间降序" :value="2"></el-option>
  67. </el-select>
  68. </div>
  69. <el-button style="padding: 9px 15px !important;" size="small" type="primary" @click="searchs">搜索</el-button>
  70. <el-button type="warning" @click="clereSearch" v-if="searchData.store_name || searchData.storeowner || searchData.nickname || searchData.cate_id || searchData.status || searchData.time_sort" style="padding: 9px 15px !important;">清除搜索条件</el-button>
  71. </div>
  72. </div>
  73. <el-table :data="list" stripe v-loading="loading" size="small" style="margin-bottom: 15px; width: 100%">
  74. <el-table-column prop="id" label="门店ID" width="100" align="center"></el-table-column>
  75. <el-table-column label="门店名称" width="200" prop="store_name" align="center">
  76. <template slot-scope="scope">
  77. <com-image style="float: left;margin-right: 10px;" mode="aspectFill" :src="scope.row.logo_img"></com-image>
  78. <div>{{scope.row.store_name}}</div>
  79. <div>{{scope.row.shop_mobile}}</div>
  80. </template>
  81. </el-table-column>
  82. <el-table-column label="门店分类" prop="category" width="100" align="center">
  83. <template slot-scope="scope">
  84. <div>{{scope.row.category}}</div>
  85. </template>
  86. </el-table-column>
  87. <el-table-column label="门店地址" prop="store_address" width="180" align="center">
  88. <template slot-scope="scope">
  89. <div>{{scope.row.store_address}}</div>
  90. </template>
  91. </el-table-column>
  92. <el-table-column label="会员" width="150" align="center">
  93. <template slot-scope="scope">
  94. <com-image style="float: left;margin-right: 5px;" mode="aspectFill" :src="scope.row.avatar_url"></com-image>
  95. <div>{{scope.row.nickname}}</div>
  96. </template>
  97. </el-table-column>
  98. <el-table-column label="店长姓名" prop="shop_owner" width="120" align="center">
  99. <template slot-scope="scope">
  100. <div>{{scope.row.shop_owner}}</div>
  101. </template>
  102. </el-table-column>
  103. <el-table-column label="门店有效期" prop="expire_time" width="190" align="center">
  104. <template slot-scope="scope">
  105. <div>
  106. <samp v-if="scope.row.expire_time==0">无限期</samp>
  107. <samp v-else>{{scope.row.expire_time | dateTimeFormat('Y-m-d H:i:s')}} 到期</samp>
  108. </div>
  109. </template>
  110. </el-table-column>
  111. <el-table-column label="" prop="" width="160" align="center">
  112. <template slot="header">
  113. 金额
  114. <el-tooltip placement="top">
  115. <div slot="content">总收入:门店累计收入<br />当前余额:门店当前余额</div>
  116. <i class="el-icon-question grid-icon"></i>
  117. </el-tooltip>
  118. </template>
  119. <template slot-scope="scope">
  120. <div>
  121. 累计收入:<span style="color: #409EFF;">{{scope.row.total_money || 0}}</span><br />
  122. 当前余额:<span style="color: #409EFF;">{{scope.row.balance || 0}}</span>
  123. </div>
  124. </template>
  125. </el-table-column>
  126. <el-table-column label="会员总数" prop="user_nums" width="100" align="center">
  127. <template slot-scope="scope">
  128. <div>
  129. {{scope.row.user_nums}}
  130. </div>
  131. </template>
  132. </el-table-column>
  133. <el-table-column label="状态" prop="store_status" width="80" align="center">
  134. <template slot-scope="scope">
  135. <div>
  136. <span>{{storeStatus[scope.row.store_status]}}</span>
  137. </div>
  138. </template>
  139. </el-table-column>
  140. <el-table-column label="创建时间" prop="user_count" width="100" align="center">
  141. <template slot-scope="scope">
  142. <div>
  143. {{scope.row.created_at | dateTimeFormat('Y-m-d H:i:s')}}
  144. </div>
  145. </template>
  146. </el-table-column>
  147. <el-table-column label="操作" style="width: 200px;" fixed="right" align="center">
  148. <template slot-scope="scope">
  149. <el-button type="text" size="small" @click="goToDetail(scope.row.id)">详情</el-button>
  150. <!-- <el-button type="text" size="small" @click="showPromotionCode(scope.row.id)">推广码</el-button> -->
  151. <com-share-pop :url="scope.row.promotion_code">
  152. <template slot="share-button" slot-scope="data">
  153. <el-button circle size="mini" type="text" @click="showPromotionCode(scope.row.id, data.show)">推广码(公众号) </el-button>
  154. </template>
  155. </com-share-pop>
  156. <com-share-pop :url="scope.row.promotion_code">
  157. <template slot="share-button" slot-scope="data">
  158. <el-button circle size="mini" type="text" @click="showMiniPromotionCode(scope.row.id,1)">推广码(小程序) </el-button>
  159. </template>
  160. </com-share-pop>
  161. <el-button v-if="scope.row.store_status==1" type="text" size="small" @click="switchStatus(scope.$index, 3)">禁用</el-button>
  162. <el-button v-else-if="scope.row.store_status==3" type="text" size="small" @click="switchStatus(scope.$index, 1)">启用</el-button>
  163. <el-button type="text" size="small" @click="storeDelete(scope.row)">删除</el-button>
  164. <com-share-pop :url="store_collection_url" :show-share-link="false">
  165. <template slot="share-button" slot-scope="data">
  166. <el-button circle size="mini" type="text" @click="showCollectionCode(scope.row.store_collection_url, data.show)">收款码(公众号)</el-button>
  167. </template>
  168. </com-share-pop>
  169. <com-share-pop :url="scope.row.promotion_code">
  170. <template slot="share-button" slot-scope="data">
  171. <el-button circle size="mini" type="text" @click="showMiniPromotionCode(scope.row.id,2)">收款码(小程序)</el-button>
  172. </template>
  173. </com-share-pop>
  174. <com-share-pop :url="scope.row.promotion_code">
  175. <template slot="share-button" slot-scope="data">
  176. <el-button circle size="mini" type="text" @click="goToStoreMember(scope.row.id)">门店会员</el-button>
  177. </template>
  178. </com-share-pop>
  179. </template>
  180. </el-table-column>
  181. </el-table>
  182. <div style="text-align: right;margin: 20px 0;">
  183. <el-pagination @current-change="pagination" background layout="prev, pager, next" :page-count="pageCount"></el-pagination>
  184. </div>
  185. </div>
  186. </el-card>
  187. <el-dialog
  188. title=""
  189. :visible.sync="dialogVisible"
  190. width="30%"
  191. :before-close="handleClose">
  192. <el-image class="table-info-img circle" :src="applet_url">
  193. </el-image>
  194. <span slot="footer" class="dialog-footer">
  195. <el-button @click="dialogVisible = false">取 消</el-button>
  196. </span>
  197. </el-dialog>
  198. </div>
  199. <script src="/resources/unpkg/clipboard@1.0.0/dist/clipboard.min.js"></script>
  200. <script>
  201. const app = new Vue({
  202. el: '#app',
  203. data: {
  204. searchData: {
  205. cate_id: 0,
  206. nickname: '',
  207. storeowner: '',
  208. store_name: '',
  209. time_sort: '',
  210. status: 0,
  211. },
  212. currPage: 1,
  213. pageCount: 0,
  214. loading: false,
  215. dialogVisible: false,
  216. list: [],
  217. dialogLoading: false,
  218. // exportList: [],
  219. categorys: [],
  220. storeStatus: {
  221. 1: '正常',
  222. 2: '过期',
  223. 3: '禁用',
  224. 4: '冻结',
  225. 5: '同步中'
  226. },
  227. store_login_url: '', // 门店登录 url
  228. store_collection_url: '', // 门店收款码链接
  229. shareData: {
  230. url: "", //链接
  231. img: "", //二维码
  232. qrcode_filename: '', // 二维码文件名
  233. },
  234. applet_url:'',
  235. btnLoading: false,
  236. },
  237. mounted() {
  238. this.loadData();
  239. },
  240. methods: {
  241. searchs() {
  242. this.currPage = 1;
  243. this.loadData();
  244. },
  245. clereSearch() {
  246. this.searchData.cate_id = 0;
  247. this.searchData.nickname = '';
  248. this.searchData.storeowner = '';
  249. this.searchData.store_name = '';
  250. this.searchData.status = 0;
  251. this.searchData.time_sort = '';
  252. this.currPage = 1;
  253. this.loadData();
  254. },
  255. // 打开推广码弹窗
  256. showPromotionCode(id, fun) {
  257. setTimeout(() => {
  258. fun();
  259. }, 300)
  260. },
  261. showMiniPromotionCode(id,type){
  262. request({
  263. params: {
  264. r: 'store/store/show-mini-promotion-code',
  265. },
  266. data: {
  267. id: id,
  268. type:type
  269. },
  270. method: 'post'
  271. }).then(e => {
  272. if (e.data.code == 0) {
  273. this.dialogVisible = true
  274. this.applet_url = e.data.data.applet_url;
  275. } else {
  276. this.$message.error('操作失败');
  277. }
  278. }).catch(e => {
  279. this.loading = false;
  280. console.log(e);
  281. this.$message.error('网络错误');
  282. });
  283. },
  284. // 打开收款码弹窗
  285. showCollectionCode(url, fun) {
  286. this.store_collection_url = url;
  287. setTimeout(() => {
  288. fun();
  289. }, 300)
  290. },
  291. // 禁用 / 启用门店
  292. switchStatus(index, status) {
  293. let self = this;
  294. let tip = status == 1 ? '启用' : '禁用';
  295. let store_id = self.list[index].id;
  296. self.$confirm(tip + '该门店, 是否继续?', '提示', {
  297. confirmButtonText: '确定',
  298. cancelButtonText: '取消',
  299. type: 'warning'
  300. }).then(() => {
  301. self.loading = true;
  302. request({
  303. params: {
  304. r: 'store/store/switch-status',
  305. },
  306. data: {
  307. store_id: store_id,
  308. status: status,
  309. },
  310. method: 'post'
  311. }).then(e => {
  312. self.loading = false;
  313. if (e.data.code == 0) {
  314. this.list[index].store_status = status;
  315. self.$message.success('操作成功');
  316. } else {
  317. self.$message.error('操作失败');
  318. }
  319. }).catch(e => {
  320. self.loading = false;
  321. console.log(e);
  322. self.$message.error('网络错误');
  323. });
  324. }).catch(() => {
  325. });
  326. },
  327. storeDelete(row) {
  328. let self = this;
  329. self.$confirm('删除该门店, 是否继续?', '提示', {
  330. confirmButtonText: '确定',
  331. cancelButtonText: '取消',
  332. type: 'warning'
  333. }).then(() => {
  334. self.listLoading = true;
  335. request({
  336. params: {
  337. r: 'store/store/del',
  338. },
  339. method: 'post',
  340. data: {
  341. id: row.id,
  342. }
  343. }).then(e => {
  344. self.listLoading = false;
  345. if (e.data.code === 0) {
  346. self.$message.success(e.data.msg);
  347. } else {
  348. self.$message.error(e.data.msg);
  349. }
  350. }).catch(e => {
  351. console.log(e);
  352. });
  353. }).catch(() => {
  354. self.$message.info('已取消删除')
  355. });
  356. },
  357. /**
  358. * 清除小程序码
  359. */
  360. cleanMiniCode() {
  361. this.$confirm('清除小程序码, 是否继续?', '提示', {
  362. confirmButtonText: '确定',
  363. cancelButtonText: '取消',
  364. type: 'warning'
  365. }).then(() => {
  366. this.btnLoading = true;
  367. request({
  368. params: {
  369. r: 'store/store/clean-mini-code',
  370. },
  371. }).then(e => {
  372. this.btnLoading = false;
  373. if (e.data.code === 0) {
  374. this.$message.success(e.data.msg);
  375. } else {
  376. this.$message.error(e.data.msg);
  377. }
  378. }).catch(e => {
  379. this.btnLoading = false;
  380. });
  381. }).catch(() => {
  382. this.$message.info('已取消清除')
  383. });
  384. },
  385. /* confirmSubmit() {
  386. this.search.status
  387. }, */
  388. loadData() {
  389. this.loading = true;
  390. request({
  391. params: {
  392. r: 'store/store/index',
  393. page: this.currPage,
  394. ...this.searchData,
  395. },
  396. method: 'get',
  397. }).then(e => {
  398. this.loading = false;
  399. if (e.data.code == 0) {
  400. this.list = e.data.data.list;
  401. this.store_login_url = e.data.data.store_login_url;
  402. this.store_collection_url = e.data.data.store_collection_url;
  403. this.pageCount = e.data.data.page_count;
  404. // this.exportList = e.data.data.export_list;
  405. this.edit_status = e.data.data.edit_status;
  406. this.categorys = e.data.data.cate_list;
  407. } else {
  408. this.$message.error(e.data.msg);
  409. }
  410. }).catch(e => {
  411. this.loading = false;
  412. });
  413. },
  414. pagination(currentPage) {
  415. let self = this;
  416. self.currPage = currentPage;
  417. self.loadData();
  418. },
  419. addStore(id) {
  420. navigateTo({
  421. r: 'store/store/storage',
  422. });
  423. },
  424. // 跳转门店详情页
  425. goToDetail(id) {
  426. navigateTo({
  427. r: 'store/store/detail',
  428. store_id: id,
  429. });
  430. },
  431. // 跳转门店会员页
  432. goToStoreMember(id) {
  433. navigateTo({
  434. r: 'store/store-user-associated/index',
  435. store_id: id,
  436. });
  437. },
  438. }
  439. });
  440. var clipboard = new ClipboardJS('#copy_btn');
  441. var self = this;
  442. clipboard.on('success', function(e) {
  443. self.ELEMENT.Message.success('复制成功');
  444. e.clearSelection();
  445. });
  446. clipboard.on('error', function(e) {
  447. self.ELEMENT.Message.success('复制失败,请手动复制');
  448. });
  449. </script>
  450. <style>
  451. .el-tabs__header {
  452. font-size: 16px;
  453. }
  454. .table-body {
  455. padding: 20px;
  456. background-color: #fff;
  457. }
  458. .input-item {
  459. width: 250px;
  460. margin-right: 40px;
  461. }
  462. .input-item .el-input__inner {
  463. border-right: 0;
  464. }
  465. .input-item .el-input__inner:hover {
  466. border: 1px solid #dcdfe6;
  467. border-right: 0;
  468. outline: 0;
  469. }
  470. .input-item .el-input__inner:focus {
  471. border: 1px solid #dcdfe6;
  472. border-right: 0;
  473. outline: 0;
  474. }
  475. .input-item .el-input-group__append {
  476. background-color: #fff;
  477. border-left: 0;
  478. width: 10%;
  479. padding: 0;
  480. }
  481. .input-item .el-input-group__append .el-button {
  482. padding: 0;
  483. }
  484. .input-item .el-input-group__append .el-button {
  485. margin: 0;
  486. }
  487. .table-body {
  488. padding: 20px;
  489. background-color: #fff;
  490. }
  491. .table-body .el-button {
  492. padding: 0 !important;
  493. border: 0;
  494. margin: 0 5px;
  495. }
  496. .input-item {
  497. width: 250px;
  498. margin: 0 0 20px;
  499. display: inline-block;
  500. }
  501. .input-item .el-input__inner {
  502. border-right: 0;
  503. }
  504. .input-item .el-input__inner:hover {
  505. border: 1px solid #dcdfe6;
  506. border-right: 0;
  507. outline: 0;
  508. }
  509. .input-item .el-input__inner:focus {
  510. border: 1px solid #dcdfe6;
  511. border-right: 0;
  512. outline: 0;
  513. }
  514. .input-item .el-input-group__append {
  515. background-color: #fff;
  516. border-left: 0;
  517. width: 10%;
  518. padding: 0;
  519. }
  520. .input-item .el-input-group__append .el-button {
  521. padding: 0;
  522. }
  523. .input-item .el-input-group__append .el-button {
  524. margin: 0;
  525. }
  526. .batch {
  527. margin: 0 0 20px;
  528. display: inline-block;
  529. }
  530. .batch .el-button {
  531. padding: 9px 15px !important;
  532. }
  533. .el-table th>.cell {
  534. color: #333;
  535. font-weight: bold;
  536. font-size: 14px;
  537. }
  538. .el-table__footer-wrapper,
  539. .el-table__header-wrapper {
  540. border-bottom: 1.4px solid #D6D6D6;
  541. }
  542. .el-table .el-button {
  543. padding: 0 !important;
  544. border: 0;
  545. margin: 0 5px;
  546. }
  547. #app .copy .el-input-group__append {
  548. background-color: #409EFF;
  549. border-color: #409EFF;
  550. padding: 0 10px;
  551. }
  552. #app .table-body .copybtn {
  553. color: #fff;
  554. padding: 0 30px;
  555. }
  556. .el-alert {
  557. padding: 0;
  558. padding-left: 5px;
  559. padding-bottom: 5px;
  560. }
  561. .el-alert--info .el-alert__description {
  562. color: #606266;
  563. }
  564. .el-alert .el-button {
  565. margin-left: 20px;
  566. }
  567. .el-alert__content {
  568. display: flex;
  569. align-items: center;
  570. }
  571. .table-body .el-alert__title {
  572. margin-top: 5px;
  573. font-weight: 400;
  574. }
  575. .el-table th>.cell {
  576. color: #333;
  577. font-weight: bold;
  578. font-size: 14px;
  579. }
  580. .el-table__footer-wrapper,
  581. .el-table__header-wrapper {
  582. border-bottom: 1.4px solid #D6D6D6;
  583. }
  584. .item-box {
  585. margin-right: 20px;
  586. }
  587. .flex {
  588. display: flex;
  589. align-items: center;
  590. }
  591. .input-title {
  592. margin-right: 15px;
  593. }
  594. .item-box .label {
  595. margin-right: 8px;
  596. }
  597. .com-share-pop {
  598. display: inline-block;
  599. }
  600. .grid-icon {
  601. color: #999;
  602. cursor: pointer;
  603. font-size: 18px;
  604. }
  605. </style>